diff --git a/extensions/acpx/src/runtime-internals/error-format.mjs b/extensions/acpx/src/runtime-internals/error-format.mjs new file mode 100644 index 00000000000000..29b9b51b601294 --- /dev/null +++ b/extensions/acpx/src/runtime-internals/error-format.mjs @@ -0,0 +1,6 @@ +export function formatErrorMessage(error) { + if (error instanceof Error) { + return error.message || error.name || "Error"; + } + return String(error); +} diff --git a/extensions/acpx/src/runtime-internals/mcp-proxy.mjs b/extensions/acpx/src/runtime-internals/mcp-proxy.mjs index 44fb0284b592b2..9c79abfbdd744f 100644 --- a/extensions/acpx/src/runtime-internals/mcp-proxy.mjs +++ b/extensions/acpx/src/runtime-internals/mcp-proxy.mjs @@ -4,6 +4,7 @@ import { spawn } from "node:child_process"; import path from "node:path"; import { createInterface } from "node:readline"; import { pathToFileURL } from "node:url"; +import { formatErrorMessage } from "./error-format.mjs"; import { splitCommandLine } from "./mcp-command-line.mjs"; function decodePayload(argv) { @@ -94,7 +95,7 @@ function main() { child.stdout.pipe(process.stdout); child.on("error", (error) => { - process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`); + process.stderr.write(`${formatErrorMessage(error)}\n`); process.exit(1); }); diff --git a/scripts/control-ui-i18n.ts b/scripts/control-ui-i18n.ts index 24a9cdd826d7c0..c9ceadb7403f1c 100644 --- a/scripts/control-ui-i18n.ts +++ b/scripts/control-ui-i18n.ts @@ -6,6 +6,7 @@ import { homedir } from "node:os"; import path from "node:path"; import { createInterface } from "node:readline"; import { fileURLToPath, pathToFileURL } from "node:url"; +import { formatErrorMessage } from "../src/infra/errors.ts"; interface TranslationMap { [key: string]: string | TranslationMap; @@ -1317,6 +1318,6 @@ async function main() { } await main().catch((error) => { - console.error(error instanceof Error ? error.message : String(error)); + console.error(formatErrorMessage(error)); process.exit(1); }); diff --git a/scripts/dev/discord-acp-plain-language-smoke.ts b/scripts/dev/discord-acp-plain-language-smoke.ts index 199745c88ae7fc..641af712f6dffe 100644 --- a/scripts/dev/discord-acp-plain-language-smoke.ts +++ b/scripts/dev/discord-acp-plain-language-smoke.ts @@ -6,6 +6,7 @@ import { randomUUID } from "node:crypto"; import fs from "node:fs/promises"; import path from "node:path"; import { promisify } from "node:util"; +import { formatErrorMessage } from "../../src/infra/errors.ts"; function writeStdoutLine(message: string): void { process.stdout.write(`${message}\n`); @@ -540,7 +541,7 @@ async function run(): Promise { ok: false, stage: "validation", smokeId: "n/a", - error: err instanceof Error ? err.message : String(err), + error: formatErrorMessage(err), }; } @@ -672,7 +673,7 @@ async function run(): Promise { ok: false, stage: setupStage, smokeId, - error: err instanceof Error ? err.message : String(err), + error: formatErrorMessage(err), }; } @@ -830,7 +831,7 @@ const result = await run().catch( ok: false, stage: "unexpected", smokeId: "n/a", - error: err instanceof Error ? err.message : String(err), + error: formatErrorMessage(err), }), ); diff --git a/scripts/e2e/mcp-channels-harness.ts b/scripts/e2e/mcp-channels-harness.ts index 18cabd5a8d1e5a..4d3069114e835a 100644 --- a/scripts/e2e/mcp-channels-harness.ts +++ b/scripts/e2e/mcp-channels-harness.ts @@ -6,6 +6,7 @@ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js" import { WebSocket } from "ws"; import { z } from "zod"; import { PROTOCOL_VERSION } from "../../src/gateway/protocol/index.ts"; +import { formatErrorMessage } from "../../src/infra/errors.ts"; import { rawDataToString } from "../../src/infra/ws.ts"; export const ClaudeChannelNotificationSchema = z.object({ @@ -379,7 +380,7 @@ export async function maybeApprovePendingBridgePairing( pending?: Array<{ requestId?: string; role?: string }>; }>("device.pair.list", {}); } catch (error) { - const message = error instanceof Error ? error.message : String(error); + const message = formatErrorMessage(error); if (message.includes("missing scope: operator.pairing")) { return false; } diff --git a/scripts/e2e/plugins-docker.sh b/scripts/e2e/plugins-docker.sh index d57703dab2022a..805be4536c346b 100755 --- a/scripts/e2e/plugins-docker.sh +++ b/scripts/e2e/plugins-docker.sh @@ -156,6 +156,8 @@ const timeoutMs = Number(timeoutRaw) > 0 ? Number(timeoutRaw) : 45000; const gatewayCallTimeoutMs = Math.max(15000, Math.min(timeoutMs, 90000)); const retryableGatewayErrorPattern = /gateway ws open timeout|gateway connect timeout|gateway closed|ECONNREFUSED|socket hang up|gateway timeout after/i; +const formatErrorMessage = (error) => + error instanceof Error ? error.message || error.name || "Error" : String(error); const gatewayArgs = [ entry, "gateway", @@ -188,7 +190,7 @@ const callGatewayOnce = (method, params) => { }; const isRetryableGatewayError = (error) => - retryableGatewayErrorPattern.test(error instanceof Error ? error.message : String(error)); + retryableGatewayErrorPattern.test(formatErrorMessage(error)); const extractText = (messageLike) => { if (!messageLike || typeof messageLike !== "object") { @@ -316,7 +318,7 @@ async function main() { } main().catch((error) => { - console.error(error instanceof Error ? error.message : String(error)); + console.error(formatErrorMessage(error)); process.exit(1); }); NODE diff --git a/scripts/firecrawl-compare.ts b/scripts/firecrawl-compare.ts index 345a66e1670db5..16abafa905e069 100644 --- a/scripts/firecrawl-compare.ts +++ b/scripts/firecrawl-compare.ts @@ -1,5 +1,6 @@ import { fetchFirecrawlContent } from "../extensions/firecrawl/api.ts"; import { extractReadableContent } from "../src/agents/tools/web-tools.js"; +import { formatErrorMessage } from "../src/infra/errors.ts"; const DEFAULT_URLS = [ "https://en.wikipedia.org/wiki/Web_scraping", @@ -88,7 +89,7 @@ async function run() { } } catch (error) { localStatus = "error"; - localError = error instanceof Error ? error.message : String(error); + localError = formatErrorMessage(error); } console.log(`local: ${localStatus} len=${localText.length} title=${truncate(localTitle, 80)}`); @@ -125,7 +126,7 @@ async function run() { console.log(`firecrawl sample: ${truncate(firecrawl.text)}`); } } catch (error) { - const message = error instanceof Error ? error.message : String(error); + const message = formatErrorMessage(error); console.log(`firecrawl: error ${message}`); } } diff --git a/scripts/lib/error-format.mjs b/scripts/lib/error-format.mjs new file mode 100644 index 00000000000000..29b9b51b601294 --- /dev/null +++ b/scripts/lib/error-format.mjs @@ -0,0 +1,6 @@ +export function formatErrorMessage(error) { + if (error instanceof Error) { + return error.message || error.name || "Error"; + } + return String(error); +} diff --git a/scripts/openclaw-npm-postpublish-verify.ts b/scripts/openclaw-npm-postpublish-verify.ts index 8ea232fb81f86f..08fcee08343aea 100644 --- a/scripts/openclaw-npm-postpublish-verify.ts +++ b/scripts/openclaw-npm-postpublish-verify.ts @@ -5,6 +5,7 @@ import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { pathToFileURL } from "node:url"; +import { formatErrorMessage } from "../src/infra/errors.ts"; import { BUNDLED_RUNTIME_SIDECAR_PATHS } from "../src/plugins/runtime-sidecar-paths.ts"; import { parseReleaseVersion, resolveNpmCommandInvocation } from "./openclaw-npm-release-check.ts"; @@ -141,9 +142,7 @@ if (entrypoint !== null && import.meta.url === entrypoint) { try { main(); } catch (error) { - console.error( - `openclaw-npm-postpublish-verify: ${error instanceof Error ? error.message : String(error)}`, - ); + console.error(`openclaw-npm-postpublish-verify: ${formatErrorMessage(error)}`); process.exitCode = 1; } } diff --git a/scripts/openclaw-prepack.ts b/scripts/openclaw-prepack.ts index 1433cc298aa176..c3b906729214cd 100644 --- a/scripts/openclaw-prepack.ts +++ b/scripts/openclaw-prepack.ts @@ -3,6 +3,7 @@ import { spawnSync } from "node:child_process"; import { existsSync, readdirSync } from "node:fs"; import { pathToFileURL } from "node:url"; +import { formatErrorMessage } from "../src/infra/errors.ts"; const skipPrepackPreparedEnv = "OPENCLAW_PREPACK_PREPARED"; const requiredPreparedPathGroups = [ @@ -90,7 +91,7 @@ function ensurePreparedArtifacts(): void { console.error(`prepack: ${error}`); } } catch (error) { - const message = error instanceof Error ? error.message : String(error); + const message = formatErrorMessage(error); console.error(`prepack: failed to verify prepared artifacts: ${message}`); } diff --git a/scripts/profile-extension-memory.mjs b/scripts/profile-extension-memory.mjs index 3c7c1b50562a42..3f34611c7142f9 100644 --- a/scripts/profile-extension-memory.mjs +++ b/scripts/profile-extension-memory.mjs @@ -4,6 +4,7 @@ import { spawn } from "node:child_process"; import { existsSync, mkdtempSync, readdirSync, rmSync, writeFileSync } from "node:fs"; import os from "node:os"; import path from "node:path"; +import { formatErrorMessage } from "./lib/error-format.mjs"; const DEFAULT_CONCURRENCY = 6; const DEFAULT_TIMEOUT_MS = 90_000; @@ -354,6 +355,6 @@ async function main() { try { await main(); } catch (error) { - console.error(`[extension-memory] ${error instanceof Error ? error.message : String(error)}`); + console.error(`[extension-memory] ${formatErrorMessage(error)}`); process.exit(1); } diff --git a/scripts/run-vitest-profile.mjs b/scripts/run-vitest-profile.mjs index 007a27782d0ce7..18ad559ca3d148 100644 --- a/scripts/run-vitest-profile.mjs +++ b/scripts/run-vitest-profile.mjs @@ -3,6 +3,7 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { pathToFileURL } from "node:url"; +import { formatErrorMessage } from "./lib/error-format.mjs"; export function parseArgs(argv) { const args = { @@ -106,7 +107,7 @@ if (isMain) { try { main(); } catch (error) { - console.error(error instanceof Error ? error.message : String(error)); + console.error(formatErrorMessage(error)); process.exit(1); } } diff --git a/scripts/sqlite-vec-smoke.mjs b/scripts/sqlite-vec-smoke.mjs index 19a3c5784d8917..499cebba8ab04a 100644 --- a/scripts/sqlite-vec-smoke.mjs +++ b/scripts/sqlite-vec-smoke.mjs @@ -1,5 +1,6 @@ import { DatabaseSync } from "node:sqlite"; import { load, getLoadablePath } from "sqlite-vec"; +import { formatErrorMessage } from "./lib/error-format.mjs"; function vec(values) { return Buffer.from(new Float32Array(values).buffer); @@ -10,7 +11,7 @@ const db = new DatabaseSync(":memory:", { allowExtension: true }); try { load(db); } catch (err) { - const message = err instanceof Error ? err.message : String(err); + const message = formatErrorMessage(err); console.error("sqlite-vec load failed:"); console.error(message); console.error("expected extension path:", getLoadablePath()); diff --git a/scripts/test-extension.mjs b/scripts/test-extension.mjs index d1f44c9599601a..b7e350feb34871 100644 --- a/scripts/test-extension.mjs +++ b/scripts/test-extension.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node +import { formatErrorMessage } from "./lib/error-format.mjs"; import { resolveExtensionTestPlan } from "./lib/extension-test-plan.mjs"; import { isDirectScriptRun, runVitestBatch } from "./lib/vitest-batch-runner.mjs"; @@ -31,7 +32,7 @@ async function run() { plan = resolveExtensionTestPlan({ cwd: process.cwd(), targetArg }); } catch (error) { printUsage(); - console.error(error instanceof Error ? error.message : String(error)); + console.error(formatErrorMessage(error)); process.exit(1); } diff --git a/scripts/test-live-media.ts b/scripts/test-live-media.ts index 7a73ae51432902..19f131207974d8 100644 --- a/scripts/test-live-media.ts +++ b/scripts/test-live-media.ts @@ -4,6 +4,7 @@ import { spawn, type ChildProcess } from "node:child_process"; import { createRequire } from "node:module"; import { pathToFileURL } from "node:url"; import { collectProviderApiKeys } from "../src/agents/live-auth-keys.js"; +import { formatErrorMessage } from "../src/infra/errors.ts"; import { loadShellEnvFallback } from "../src/infra/shell-env.js"; import { getProviderEnvVars } from "../src/secrets/provider-env-vars.js"; type SpawnPnpmRunner = (params: { @@ -365,7 +366,7 @@ if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) { runCli(process.argv.slice(2)) .then((code) => process.exit(code)) .catch((error) => { - console.error(error instanceof Error ? error.message : String(error)); + console.error(formatErrorMessage(error)); process.exit(1); }); } diff --git a/scripts/ts-topology.ts b/scripts/ts-topology.ts index 0f4e59344cbad9..c0b370c5a18f9c 100644 --- a/scripts/ts-topology.ts +++ b/scripts/ts-topology.ts @@ -1,5 +1,6 @@ #!/usr/bin/env node import path from "node:path"; +import { formatErrorMessage } from "../src/infra/errors.ts"; import { analyzeTopology } from "./lib/ts-topology/analyze.js"; import { renderTextReport } from "./lib/ts-topology/reports.js"; import { @@ -136,7 +137,7 @@ export async function main(argv: string[], io: IoLike = process): Promise