fix: tame invalid config logging

This commit is contained in:
Peter Steinberger
2026-01-21 01:58:08 +00:00
parent 27acfa59c5
commit e81ca7ab00
7 changed files with 30 additions and 13 deletions

View File

@@ -31,6 +31,7 @@ Docs: https://docs.clawd.bot
- Gateway: clarify unauthorized handshake responses with token/password mismatch guidance.
- Gateway: preserve restart wake routing + thread replies across restarts. (#1337) — thanks @John-Rood.
- Gateway: reschedule per-agent heartbeats on config hot reload without restarting the runner.
- Config: log invalid config issues once per run and keep invalid-config errors stackless.
- UI: keep config form enums typed, preserve empty strings, protect sensitive defaults, and deepen config search. (#1315) — thanks @MaudeBot.
- UI: preserve ordered list numbering in chat markdown. (#1341) — thanks @bradleypriest.
- UI: allow Control UI to read gatewayUrl from URL params for remote WebSocket targets. (#1342) — thanks @ameno-.

View File

@@ -7,6 +7,7 @@ import { normalizeEnv } from "../infra/env.js";
import { isMainModule } from "../infra/is-main.js";
import { ensureClawdbotCliOnPath } from "../infra/path-env.js";
import { assertSupportedRuntime } from "../infra/runtime-guard.js";
import { formatUncaughtError } from "../infra/errors.js";
import { installUnhandledRejectionHandler } from "../infra/unhandled-rejections.js";
import { enableConsoleCapture } from "../logging.js";
import { tryRouteCli } from "./route.js";
@@ -42,7 +43,7 @@ export async function runCli(argv: string[] = process.argv) {
installUnhandledRejectionHandler();
process.on("uncaughtException", (error) => {
console.error("[clawdbot] Uncaught exception:", error.stack ?? error.message);
console.error("[clawdbot] Uncaught exception:", formatUncaughtError(error));
process.exit(1);
});

View File

@@ -57,6 +57,7 @@ const SHELL_ENV_EXPECTED_KEYS = [
];
const CONFIG_BACKUP_COUNT = 5;
const loggedInvalidConfigs = new Set<string>();
export type ParseConfigJson5Result = { ok: true; parsed: unknown } | { ok: false; error: string };
@@ -244,8 +245,14 @@ export function createConfigIO(overrides: ConfigIoDeps = {}) {
const details = validated.issues
.map((iss) => `- ${iss.path || "<root>"}: ${iss.message}`)
.join("\n");
deps.logger.error(`Invalid config:\\n${details}`);
throw new Error("Invalid config");
if (!loggedInvalidConfigs.has(configPath)) {
loggedInvalidConfigs.add(configPath);
deps.logger.error(`Invalid config:\\n${details}`);
}
const error = new Error("Invalid config");
(error as { code?: string; details?: string }).code = "INVALID_CONFIG";
(error as { code?: string; details?: string }).details = details;
throw error;
}
if (validated.warnings.length > 0) {
const details = validated.warnings

View File

@@ -28,6 +28,7 @@ import {
PortInUseError,
} from "./infra/ports.js";
import { assertSupportedRuntime } from "./infra/runtime-guard.js";
import { formatUncaughtError } from "./infra/errors.js";
import { installUnhandledRejectionHandler } from "./infra/unhandled-rejections.js";
import { enableConsoleCapture } from "./logging.js";
import { runCommandWithTimeout, runExec } from "./process/exec.js";
@@ -82,15 +83,12 @@ if (isMain) {
installUnhandledRejectionHandler();
process.on("uncaughtException", (error) => {
console.error("[clawdbot] Uncaught exception:", error.stack ?? error.message);
console.error("[clawdbot] Uncaught exception:", formatUncaughtError(error));
process.exit(1);
});
void program.parseAsync(process.argv).catch((err) => {
console.error(
"[clawdbot] CLI failed:",
err instanceof Error ? (err.stack ?? err.message) : err,
);
console.error("[clawdbot] CLI failed:", formatUncaughtError(err));
process.exit(1);
});
}

View File

@@ -20,3 +20,13 @@ export function formatErrorMessage(err: unknown): string {
return Object.prototype.toString.call(err);
}
}
export function formatUncaughtError(err: unknown): string {
if (extractErrorCode(err) === "INVALID_CONFIG") {
return formatErrorMessage(err);
}
if (err instanceof Error) {
return err.stack ?? err.message ?? err.name;
}
return formatErrorMessage(err);
}

View File

@@ -1,5 +1,7 @@
import process from "node:process";
import { formatUncaughtError } from "./errors.js";
type UnhandledRejectionHandler = (reason: unknown) => boolean;
const handlers = new Set<UnhandledRejectionHandler>();
@@ -28,10 +30,7 @@ export function isUnhandledRejectionHandled(reason: unknown): boolean {
export function installUnhandledRejectionHandler(): void {
process.on("unhandledRejection", (reason, _promise) => {
if (isUnhandledRejectionHandled(reason)) return;
console.error(
"[clawdbot] Unhandled promise rejection:",
reason instanceof Error ? (reason.stack ?? reason.message) : reason,
);
console.error("[clawdbot] Unhandled promise rejection:", formatUncaughtError(reason));
process.exit(1);
});
}

View File

@@ -55,6 +55,7 @@ async function main() {
const { assertSupportedRuntime } = await import("../infra/runtime-guard.js");
assertSupportedRuntime();
const { formatUncaughtError } = await import("../infra/errors.js");
const { installUnhandledRejectionHandler } = await import("../infra/unhandled-rejections.js");
const { buildProgram } = await import("../cli/program.js");
@@ -63,7 +64,7 @@ async function main() {
installUnhandledRejectionHandler();
process.on("uncaughtException", (error) => {
console.error("[clawdbot] Uncaught exception:", error.stack ?? error.message);
console.error("[clawdbot] Uncaught exception:", formatUncaughtError(error));
process.exit(1);
});