diff --git a/src/cli/cli-utils.test.ts b/src/cli/cli-utils.test.ts index 7578b7f84db2..776b4a7b8788 100644 --- a/src/cli/cli-utils.test.ts +++ b/src/cli/cli-utils.test.ts @@ -1,6 +1,7 @@ // CLI utility tests cover shared command helpers, option parsing, and output formatting. import { Command } from "commander"; import { describe, expect, it, vi } from "vitest"; +import { runCommandWithRuntime } from "./cli-utils.js"; import { registerDnsCli } from "./dns-cli.js"; import { parseByteSize } from "./parse-bytes.js"; import { parseDurationMs } from "./parse-duration.js"; @@ -33,6 +34,33 @@ describe("waitForever", () => { }); }); +describe("runCommandWithRuntime", () => { + it("surfaces cause chains and error codes through the default runtime", async () => { + const messages: string[] = []; + const exits: number[] = []; + const cause = Object.assign(new Error("invalid onRequestStart method"), { + code: "UND_ERR_INVALID_ARG", + }); + const fetchError = Object.assign(new TypeError("fetch failed"), { cause }); + + await runCommandWithRuntime( + { + error: (message) => messages.push(message), + exit: (code) => exits.push(code), + }, + async () => { + throw fetchError; + }, + ); + + expect(messages).toHaveLength(1); + expect(messages[0]).toContain("TypeError: fetch failed"); + expect(messages[0]).toContain("invalid onRequestStart method"); + expect(messages[0]).toContain("UND_ERR_INVALID_ARG"); + expect(exits).toEqual([1]); + }); +}); + describe("shouldSkipRespawnForArgv", () => { it.each([ { argv: ["node", "openclaw", "--help"] }, diff --git a/src/cli/cli-utils.ts b/src/cli/cli-utils.ts index c85e2132622c..f4945746ea12 100644 --- a/src/cli/cli-utils.ts +++ b/src/cli/cli-utils.ts @@ -32,6 +32,13 @@ export async function withManager(params: { } } +function formatCommandRuntimeError(err: unknown): string { + if (err instanceof Error) { + return formatErrorMessage(new Error(String(err), { cause: err.cause })); + } + return formatErrorMessage(err); +} + export async function runCommandWithRuntime( runtime: { error: (message: string) => void; exit: (code: number) => void }, action: () => Promise, @@ -44,7 +51,7 @@ export async function runCommandWithRuntime( onError(err); return; } - runtime.error(String(err)); + runtime.error(formatCommandRuntimeError(err)); runtime.exit(1); } } diff --git a/src/infra/errors.ts b/src/infra/errors.ts index e5951c1c489b..1bd0ba49c287 100644 --- a/src/infra/errors.ts +++ b/src/infra/errors.ts @@ -86,6 +86,10 @@ export function formatErrorMessage(err: unknown): string { seen.add(cause); if (cause instanceof Error) { appendCauseMessage(cause.message); + const code = extractErrorCode(cause); + if (code) { + appendCauseMessage(code); + } cause = cause.cause; } else if (typeof cause === "string") { appendCauseMessage(cause);