refactor: dedupe runtime error formatting

This commit is contained in:
Peter Steinberger
2026-04-07 01:58:25 +01:00
parent 9b7c0bf8e9
commit d4360f8068
12 changed files with 26 additions and 17 deletions

View File

@@ -8,6 +8,7 @@ import {
resolveMainSessionKey,
} from "../../config/sessions/main-session.js";
import { sleepWithAbort } from "../../infra/backoff.js";
import { formatErrorMessage } from "../../infra/errors.js";
import type { OutboundDeliveryResult } from "../../infra/outbound/deliver.js";
import { normalizeTargetForProvider } from "../../infra/outbound/target-normalization.js";
import { logWarn, logError } from "../../logger.js";
@@ -273,7 +274,7 @@ async function queueCronAwarenessSystemEvent(params: {
});
} catch (err) {
logWarn(
`[cron:${params.jobId}] failed to queue isolated cron awareness for the main session: ${err instanceof Error ? err.message : String(err)}`,
`[cron:${params.jobId}] failed to queue isolated cron awareness for the main session: ${formatErrorMessage(err)}`,
);
}
}
@@ -488,7 +489,7 @@ export async function dispatchCronDelivery(
? (err: unknown, _payload: unknown) => {
hadPartialFailure = true;
logError(
`[cron:${params.job.id}] delivery payload failed (bestEffort): ${err instanceof Error ? err.message : String(err)}`,
`[cron:${params.job.id}] delivery payload failed (bestEffort): ${formatErrorMessage(err)}`,
);
}
: undefined;
@@ -551,9 +552,7 @@ export async function dispatchCronDelivery(
...params.telemetry,
});
}
logError(
`[cron:${params.job.id}] delivery failed (bestEffort): ${err instanceof Error ? err.message : String(err)}`,
);
logError(`[cron:${params.job.id}] delivery failed (bestEffort): ${formatErrorMessage(err)}`);
return null;
}
};

View File

@@ -4,6 +4,7 @@ import type { OpenClawConfig } from "../../config/config.js";
import { resolveAgentMainSessionKey } from "../../config/sessions/main-session.js";
import { resolveStorePath } from "../../config/sessions/paths.js";
import { loadSessionStore } from "../../config/sessions/store-load.js";
import { formatErrorMessage } from "../../infra/errors.js";
import { maybeResolveIdLikeTarget } from "../../infra/outbound/target-resolver.js";
import { tryResolveLoadedOutboundTarget } from "../../infra/outbound/targets-loaded.js";
import { resolveSessionDeliveryTarget } from "../../infra/outbound/targets-session.js";
@@ -106,7 +107,7 @@ export async function resolveDeliveryTarget(
const selection = await resolveMessageChannelSelection({ cfg });
fallbackChannel = selection.channel;
} catch (err) {
const detail = err instanceof Error ? err.message : String(err);
const detail = formatErrorMessage(err);
channelResolutionError = new Error(
`${detail} Set delivery.channel explicitly or use a main session with a previous channel.`,
);

View File

@@ -2,6 +2,7 @@ import type { AuthProfileStore } from "../agents/auth-profiles.js";
import { describeFailoverError, isFailoverError } from "../agents/failover-error.js";
import type { FallbackAttempt } from "../agents/model-fallback.types.js";
import type { OpenClawConfig } from "../config/config.js";
import { formatErrorMessage } from "../infra/errors.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import {
buildNoCapabilityModelConfiguredMessage,
@@ -158,7 +159,7 @@ export async function generateImage(
attempts.push({
provider: candidate.provider,
model: candidate.model,
error: described?.message ?? (err instanceof Error ? err.message : String(err)),
error: described?.message ?? formatErrorMessage(err),
reason: described?.reason,
status: described?.status,
code: described?.code,

View File

@@ -3,6 +3,7 @@ import type { GatewayClient } from "../gateway/client.js";
import { createOperatorApprovalsGatewayClient } from "../gateway/operator-approvals-client.js";
import type { EventFrame } from "../gateway/protocol/index.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import { formatErrorMessage } from "./errors.js";
import type { ExecApprovalRequest, ExecApprovalResolved } from "./exec-approvals.js";
import type { PluginApprovalRequest, PluginApprovalResolved } from "./plugin-approvals.js";
@@ -75,7 +76,7 @@ export function createExecApprovalChannelRuntime<
const spawn = (label: string, promise: Promise<void>): void => {
void promise.catch((err: unknown) => {
const message = err instanceof Error ? err.message : String(err);
const message = formatErrorMessage(err);
log.error(`${label}: ${message}`);
});
};

View File

@@ -1,5 +1,6 @@
import { EnvHttpProxyAgent, ProxyAgent, fetch as undiciFetch } from "undici";
import { logWarn } from "../../logger.js";
import { formatErrorMessage } from "../errors.js";
import { hasEnvHttpProxyConfigured } from "./proxy-env.js";
export const PROXY_FETCH_PROXY_URL = Symbol.for("openclaw.proxyFetch.proxyUrl");
@@ -66,7 +67,7 @@ export function resolveProxyFetchFromEnv(
}) as unknown as Promise<Response>) as typeof fetch;
} catch (err) {
logWarn(
`Proxy env var set but agent creation failed — falling back to direct fetch: ${err instanceof Error ? err.message : String(err)}`,
`Proxy env var set but agent creation failed — falling back to direct fetch: ${formatErrorMessage(err)}`,
);
return undefined;
}

View File

@@ -5,6 +5,7 @@ import {
signDevicePayload,
type DeviceIdentity,
} from "./device-identity.js";
import { formatErrorMessage } from "./errors.js";
export type ApnsRelayPushType = "alert" | "background";
@@ -145,7 +146,7 @@ export function resolveApnsRelayConfigFromEnv(
},
};
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
const message = formatErrorMessage(err);
return {
ok: false,
error: `invalid ${baseUrlSource} (${baseUrl}): ${message}`,

View File

@@ -4,6 +4,7 @@ import http2 from "node:http2";
import path from "node:path";
import { resolveStateDir } from "../config/paths.js";
import type { DeviceIdentity } from "./device-identity.js";
import { formatErrorMessage } from "./errors.js";
import { createAsyncLock, readJsonFile, writeJsonAtomic } from "./json-files.js";
import {
type ApnsRelayConfig,
@@ -635,7 +636,7 @@ export async function resolveApnsAuthConfigFromEnv(
},
};
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
const message = formatErrorMessage(err);
return {
ok: false,
error: `failed reading OPENCLAW_APNS_PRIVATE_KEY_PATH (${keyPath}): ${message}`,

View File

@@ -5,6 +5,7 @@ import path from "node:path";
import { quoteCmdScriptArg } from "../daemon/cmd-argv.js";
import { resolveGatewayWindowsTaskName } from "../daemon/constants.js";
import { resolveTaskScriptPath } from "../daemon/schtasks.js";
import { formatErrorMessage } from "./errors.js";
import type { RestartAttempt } from "./restart.js";
import { resolvePreferredOpenClawTmpDir } from "./tmp-openclaw-dir.js";
@@ -78,7 +79,7 @@ export function relaunchGatewayScheduledTask(env: NodeJS.ProcessEnv = process.en
return {
ok: false,
method: "schtasks",
detail: err instanceof Error ? err.message : String(err),
detail: formatErrorMessage(err),
tried: [`schtasks /Run /TN "${taskName}"`],
};
}

View File

@@ -2,6 +2,7 @@ import type { AuthProfileStore } from "../agents/auth-profiles.js";
import { describeFailoverError, isFailoverError } from "../agents/failover-error.js";
import type { FallbackAttempt } from "../agents/model-fallback.types.js";
import type { OpenClawConfig } from "../config/config.js";
import { formatErrorMessage } from "../infra/errors.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import {
buildNoCapabilityModelConfiguredMessage,
@@ -139,7 +140,7 @@ export async function generateMusic(
attempts.push({
provider: candidate.provider,
model: candidate.model,
error: described?.message ?? (err instanceof Error ? err.message : String(err)),
error: described?.message ?? formatErrorMessage(err),
reason: described?.reason,
status: described?.status,
code: described?.code,

View File

@@ -1,3 +1,4 @@
import { formatErrorMessage } from "../infra/errors.js";
import { runCommandWithTimeout } from "../process/exec.js";
export type PluginCommandRunResult = {
@@ -40,7 +41,7 @@ export async function runPluginCommandWithTimeout(
return {
code: 1,
stdout: "",
stderr: error instanceof Error ? error.message : String(error),
stderr: formatErrorMessage(error),
};
}
}

View File

@@ -1,4 +1,5 @@
import type { IncomingMessage, ServerResponse } from "node:http";
import { formatErrorMessage } from "../infra/errors.js";
import {
installRequestBodyLimitGuard,
isRequestBodyLimitError,
@@ -268,8 +269,7 @@ export async function readWebhookBodyOrReject(params: {
return respondWebhookBodyReadError({
res: params.res,
code: "INVALID_BODY",
invalidMessage:
params.invalidBodyMessage ?? (error instanceof Error ? error.message : String(error)),
invalidMessage: params.invalidBodyMessage ?? formatErrorMessage(error),
});
}
}

View File

@@ -2,6 +2,7 @@ import type { AuthProfileStore } from "../agents/auth-profiles.js";
import { describeFailoverError, isFailoverError } from "../agents/failover-error.js";
import type { FallbackAttempt } from "../agents/model-fallback.types.js";
import type { OpenClawConfig } from "../config/config.js";
import { formatErrorMessage } from "../infra/errors.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import {
buildNoCapabilityModelConfiguredMessage,
@@ -182,7 +183,7 @@ export async function generateVideo(
attempts.push({
provider: candidate.provider,
model: candidate.model,
error: described?.message ?? (err instanceof Error ? err.message : String(err)),
error: described?.message ?? formatErrorMessage(err),
reason: described?.reason,
status: described?.status,
code: described?.code,