fix(config): finalize runtime overrides when config is missing (#128600)

This commit is contained in:
Peter Steinberger
2026-08-24 01:21:18 -07:00
committed by GitHub
parent 53dcaaedec
commit 628b7a2f91
2 changed files with 11 additions and 25 deletions
+4
View File
@@ -14,6 +14,7 @@ import {
readConfigFileSnapshot,
readSourceConfigBestEffort,
} from "./config.js";
import { resetConfigOverrides, setConfigOverride } from "./runtime-overrides.js";
import { withTempHome, writeOpenClawConfig } from "./test-helpers.js";
type ConfigHealthDatabase = Pick<OpenClawStateKyselyDatabase, "config_health_entries">;
@@ -33,6 +34,7 @@ function readConfigHealthRow(env: NodeJS.ProcessEnv, configPath: string) {
describe("readBestEffortConfig", () => {
afterEach(() => {
closeOpenClawStateDatabaseForTest();
resetConfigOverrides();
});
it("can read snapshots without updating config observation state", async () => {
@@ -230,6 +232,7 @@ describe("readBestEffortConfig", () => {
it("materializes fresh-install defaults when the config file is missing", async () => {
await withTempHome(async () => {
const { loadConfig } = await import("./io.runtime.js");
expect(setConfigOverride("logging.level", "warn").ok).toBe(true);
const snapshot = await readConfigFileSnapshot({ observe: false });
const loaded = loadConfig({ pin: false, skipPluginValidation: true });
@@ -240,6 +243,7 @@ describe("readBestEffortConfig", () => {
// stays provider-conditional, so compaction is the parity signal here).
expect(snapshot.config.agents?.defaults?.compaction?.mode).toBe("safeguard");
expect(loaded.agents?.defaults?.compaction?.mode).toBe("safeguard");
expect(loaded.logging?.level).toBe("warn");
});
});
+7 -25
View File
@@ -1,10 +1,4 @@
import { formatErrorMessage } from "../infra/errors.js";
import {
loadShellEnvFallback,
resolveShellEnvFallbackTimeoutMs,
shouldDeferShellEnvFallback,
shouldEnableShellEnvFallback,
} from "../infra/shell-env.js";
import { DuplicateAgentDirError, findDuplicateAgentDirs } from "./agent-dirs.js";
import type { ConfigIoContext } from "./io.context.js";
import { materializeConfigForLoad } from "./io.context.js";
@@ -28,7 +22,6 @@ import {
warnOnConfigMiskeys,
} from "./io.warnings.js";
import { migrateLegacyContextBudgetConfig, migratePersistedImplicitMainRoster } from "./legacy.js";
import { resolveShellEnvExpectedKeys } from "./shell-env-expected-keys.js";
import type { OpenClawConfig } from "./types.js";
import { validateConfigObjectWithPlugins } from "./validation.js";
@@ -43,27 +36,16 @@ export function loadConfigFromContext(
envBeforeRead = snapshotEnv(deps.env);
if (!deps.fs.existsSync(configPath)) {
loggedConfigWarningFingerprints.delete(configPath);
if (
context.options.shellEnvFallback !== "defer" &&
shouldEnableShellEnvFallback(deps.env) &&
!shouldDeferShellEnvFallback(deps.env)
) {
loadShellEnvFallback({
enabled: true,
env: deps.env,
expectedKeys: resolveShellEnvExpectedKeys(deps.env),
logger: deps.logger,
timeoutMs: resolveShellEnvFallbackTimeoutMs(deps.env),
});
}
// A missing config is the fresh-install default path: materialize the
// same runtime defaults an empty {} config gets, or out-of-box behavior
// (compaction safeguard, session/cron defaults) silently diverges.
return materializeConfigForLoad(
context,
coerceConfig(migratePersistedImplicitMainRoster({}).config),
{},
undefined,
return context.finalizeLoadedRuntimeConfig(
materializeConfigForLoad(
context,
coerceConfig(migratePersistedImplicitMainRoster({}).config),
{},
undefined,
),
);
}
const raw = deps.fs.readFileSync(configPath, "utf-8");