fix(config): preserve config data when validation fails

When readConfigFileSnapshot encounters validation errors, it now:
1. Returns the resolved config data instead of empty object
2. Uses passthrough() on main schema to preserve unknown fields

This prevents config loss when:
- User has custom/unknown fields
- Legacy config issues are detected but config is otherwise valid
- Zod schema does not recognize newer fields

Fixes config being overwritten with empty object on validation failure.
This commit is contained in:
Muhammed Mukhthar CM
2026-01-12 06:22:59 +00:00
committed by Peter Steinberger
parent b32f6a0e00
commit 20ba8d4891
3 changed files with 45 additions and 1 deletions

View File

@@ -1685,3 +1685,46 @@ describe("multi-agent agentDir validation", () => {
});
});
});
describe("config preservation on validation failure", () => {
it("preserves unknown fields via passthrough", async () => {
vi.resetModules();
const { validateConfigObject } = await import("./config.js");
const res = validateConfigObject({
agents: { list: [{ id: "pi" }] },
customUnknownField: { nested: "value" },
});
expect(res.ok).toBe(true);
expect(
(res as { config: Record<string, unknown> }).config.customUnknownField,
).toEqual({
nested: "value",
});
});
it("preserves config data when validation fails", async () => {
await withTempHome(async (home) => {
const configDir = path.join(home, ".clawdbot");
await fs.mkdir(configDir, { recursive: true });
await fs.writeFile(
path.join(configDir, "clawdbot.json"),
JSON.stringify({
agents: { list: [{ id: "pi" }] },
routing: { allowFrom: ["+15555550123"] },
customData: { preserved: true },
}),
"utf-8",
);
vi.resetModules();
const { readConfigFileSnapshot } = await import("./config.js");
const snap = await readConfigFileSnapshot();
expect(snap.valid).toBe(false);
expect(snap.legacyIssues.length).toBeGreaterThan(0);
expect((snap.config as Record<string, unknown>).customData).toEqual({
preserved: true,
});
});
});
});

View File

@@ -296,7 +296,7 @@ export function createConfigIO(overrides: ConfigIoDeps = {}) {
raw,
parsed: parsedRes.parsed,
valid: false,
config: {},
config: resolved as ClawdbotConfig,
issues: validated.issues,
legacyIssues,
};

View File

@@ -1757,6 +1757,7 @@ export const ClawdbotSchema = z
})
.optional(),
})
.passthrough()
.superRefine((cfg, ctx) => {
const agents = cfg.agents?.list ?? [];
if (agents.length === 0) return;