refactor: dedupe core error formatting call sites

This commit is contained in:
Peter Steinberger
2026-04-07 04:05:23 +01:00
parent a03e430248
commit bbe9b7ba15
12 changed files with 34 additions and 53 deletions

View File

@@ -143,7 +143,7 @@ import {
} from "./tool-schema-runtime.js";
import { splitSdkTools } from "./tool-split.js";
import type { EmbeddedPiCompactResult } from "./types.js";
import { describeUnknownError, mapThinkingLevel } from "./utils.js";
import { mapThinkingLevel } from "./utils.js";
import { flushPendingToolResultsAfterIdle } from "./wait-for-idle-before-flush.js";
export type CompactEmbeddedPiSessionParams = {
@@ -395,7 +395,7 @@ export async function compactEmbeddedPiSessionDirect(
authStorage.setRuntimeApiKey(runtimeModel.provider, runtimeApiKey);
}
} catch (err) {
const reason = describeUnknownError(err);
const reason = formatErrorMessage(err);
return fail(reason);
}
@@ -1103,7 +1103,7 @@ export async function compactEmbeddedPiSessionDirect(
};
} catch (err) {
const fallbackThinking = pickFallbackThinkingLevel({
message: describeUnknownError(err),
message: formatErrorMessage(err),
attempted: attemptedThinking,
});
if (fallbackThinking) {
@@ -1149,7 +1149,7 @@ export async function compactEmbeddedPiSessionDirect(
}
} catch (err) {
const reason = resolveCompactionFailureReason({
reason: describeUnknownError(err),
reason: formatErrorMessage(err),
safeguardCancelReason: consumeCompactionSafeguardCancelReason(compactionSessionManager),
});
return fail(reason);

View File

@@ -7,6 +7,7 @@ import {
} from "../../context-engine/index.js";
import { emitAgentPlanEvent } from "../../infra/agent-events.js";
import { sleepWithAbort } from "../../infra/backoff.js";
import { formatErrorMessage } from "../../infra/errors.js";
import { getGlobalHookRunner } from "../../plugins/hook-runner-global.js";
import { enqueueCommandInLane } from "../../process/command-queue.js";
import { sanitizeForLog } from "../../terminal/ansi.js";
@@ -101,7 +102,6 @@ import {
} from "./tool-result-truncation.js";
import type { EmbeddedPiAgentMeta, EmbeddedPiRunResult } from "./types.js";
import { createUsageAccumulator, mergeUsageIntoAccumulator } from "./usage-accumulator.js";
import { describeUnknownError } from "./utils.js";
type ApiKeyInfo = ResolvedProviderAuth;
@@ -139,7 +139,7 @@ function backfillSessionKey(params: {
return resolved.sessionKey?.trim() || undefined;
} catch (err) {
log.warn(
`[backfillSessionKey] Failed to resolve sessionKey for sessionId=${redactRunIdentifier(sanitizeForLog(params.sessionId))}: ${describeUnknownError(err)}`,
`[backfillSessionKey] Failed to resolve sessionKey for sessionId=${redactRunIdentifier(sanitizeForLog(params.sessionId))}: ${formatErrorMessage(err)}`,
);
return undefined;
}
@@ -850,7 +850,7 @@ export async function runEmbeddedPiAgent(
const contextOverflowError = !aborted
? (() => {
if (promptError) {
const errorText = describeUnknownError(promptError);
const errorText = formatErrorMessage(promptError);
if (isLikelyContextOverflowError(errorText)) {
return { text: errorText, source: "promptError" as const };
}
@@ -1092,7 +1092,7 @@ export async function runEmbeddedPiAgent(
const promptErrorDetails = normalizedPromptFailover
? describeFailoverError(normalizedPromptFailover)
: describeFailoverError(promptError);
const errorText = promptErrorDetails.message || describeUnknownError(promptError);
const errorText = promptErrorDetails.message || formatErrorMessage(promptError);
if (await maybeRefreshRuntimeAuthForAuthError(errorText, runtimeAuthRetry)) {
authRetryPending = true;
continue;
@@ -1584,7 +1584,7 @@ export async function runEmbeddedPiAgent(
if (params.cleanupBundleMcpOnRunEnd === true) {
await disposeSessionMcpRuntime(params.sessionId).catch((error) => {
log.warn(
`bundle-mcp cleanup failed after run for ${params.sessionId}: ${describeUnknownError(error)}`,
`bundle-mcp cleanup failed after run for ${params.sessionId}: ${formatErrorMessage(error)}`,
);
});
}

View File

@@ -9,6 +9,7 @@ import {
import { filterHeartbeatPairs } from "../../../auto-reply/heartbeat-filter.js";
import { resolveHeartbeatPrompt } from "../../../auto-reply/heartbeat.js";
import { resolveChannelCapabilities } from "../../../config/channel-capabilities.js";
import { formatErrorMessage } from "../../../infra/errors.js";
import { resolveHeartbeatSummaryForAgent } from "../../../infra/heartbeat-summary.js";
import { getMachineDisplayName } from "../../../infra/machine-name.js";
import {
@@ -151,7 +152,7 @@ import {
normalizeProviderToolSchemas,
} from "../tool-schema-runtime.js";
import { splitSdkTools } from "../tool-split.js";
import { describeUnknownError, mapThinkingLevel } from "../utils.js";
import { mapThinkingLevel } from "../utils.js";
import { flushPendingToolResultsAfterIdle } from "../wait-for-idle-before-flush.js";
import {
assembleAttemptContextEngine,
@@ -1992,7 +1993,7 @@ export async function runEmbeddedAttempt(
provider: params.provider,
model: params.modelId,
api: params.model.api,
error: describeUnknownError(promptError),
error: formatErrorMessage(promptError),
});
} catch (entryErr) {
log.warn(`failed to persist prompt error entry: ${String(entryErr)}`);
@@ -2071,7 +2072,7 @@ export async function runEmbeddedAttempt(
{
messages: messagesSnapshot,
success: !aborted && !promptError,
error: promptError ? describeUnknownError(promptError) : undefined,
error: promptError ? formatErrorMessage(promptError) : undefined,
durationMs: Date.now() - promptStartedAt,
},
{

View File

@@ -1,5 +1,6 @@
import type { Api, Model } from "@mariozechner/pi-ai";
import type { ThinkLevel } from "../../../auto-reply/thinking.js";
import { formatErrorMessage } from "../../../infra/errors.js";
import { prepareProviderRuntimeAuth } from "../../../plugins/provider-runtime.js";
import {
type AuthProfileStore,
@@ -19,7 +20,6 @@ import {
sanitizeRuntimeProviderRequestOverrides,
} from "../../provider-request-config.js";
import { clampRuntimeAuthRefreshDelayMs } from "../../runtime-auth-refresh.js";
import { describeUnknownError } from "../utils.js";
import {
RUNTIME_AUTH_REFRESH_MARGIN_MS,
RUNTIME_AUTH_REFRESH_MIN_DELAY_MS,
@@ -186,7 +186,7 @@ export function createEmbeddedRunAuthController(params: {
.catch((err) => {
const runtimeModel = params.getRuntimeModel();
params.log.warn(
`Runtime auth refresh failed for ${runtimeModel.provider}: ${describeUnknownError(err)}`,
`Runtime auth refresh failed for ${runtimeModel.provider}: ${formatErrorMessage(err)}`,
);
throw err;
})
@@ -289,7 +289,7 @@ export function createEmbeddedRunAuthController(params: {
const fallbackMessage = `No available auth profile for ${provider} (all in cooldown or unavailable).`;
const message =
failoverParams.message?.trim() ||
(failoverParams.error ? describeUnknownError(failoverParams.error).trim() : "") ||
(failoverParams.error ? formatErrorMessage(failoverParams.error).trim() : "") ||
fallbackMessage;
const reason = resolveAuthProfileFailoverReason({
allInCooldown: failoverParams.allInCooldown,

View File

@@ -1,6 +1,5 @@
import type { ThinkingLevel } from "@mariozechner/pi-agent-core";
import type { ReasoningLevel, ThinkLevel } from "../../auto-reply/thinking.js";
import { formatErrorMessage } from "../../infra/errors.js";
export function mapThinkingLevel(level?: ThinkLevel): ThinkingLevel {
// pi-agent-core supports "xhigh"; OpenClaw enables it for specific models.
@@ -17,8 +16,4 @@ export function mapThinkingLevel(level?: ThinkLevel): ThinkingLevel {
return level;
}
export function describeUnknownError(error: unknown): string {
return formatErrorMessage(error);
}
export type { ReasoningLevel, ThinkLevel };

View File

@@ -2,6 +2,7 @@ import type { OpenClawConfig } from "../config/config.js";
import { resolveSecretInputRef } from "../config/types.secrets.js";
import { callGateway } from "../gateway/call.js";
import { validateSecretsResolveResult } from "../gateway/protocol/index.js";
import { formatErrorMessage } from "../infra/errors.js";
import { resolveManifestContractOwnerPluginId } from "../plugins/manifest-registry.js";
import {
analyzeCommandSecretAssignmentsFromSnapshot,
@@ -13,7 +14,6 @@ import { collectConfigAssignments } from "../secrets/runtime-config-collectors.j
import { createResolverContext } from "../secrets/runtime-shared.js";
import { resolveRuntimeWebTools } from "../secrets/runtime-web-tools.js";
import { assertExpectedResolvedSecretValue } from "../secrets/secret-value.js";
import { describeUnknownError } from "../secrets/shared.js";
import {
discoverConfigSecretTargetsByIds,
type DiscoveredConfigSecretTarget,
@@ -398,7 +398,7 @@ function collectInactiveSurfacePathsFromDiagnostics(diagnostics: string[]): Set<
}
function isUnsupportedSecretsResolveError(err: unknown): boolean {
const message = describeUnknownError(err).toLowerCase();
const message = formatErrorMessage(err).toLowerCase();
if (!message.includes("secrets.resolve")) {
return false;
}
@@ -460,7 +460,7 @@ async function resolveCommandSecretRefsLocally(params: {
throw error;
}
localResolutionDiagnostics.push(
`${params.commandName}: failed to resolve web tool secrets locally (${describeUnknownError(error)}).`,
`${params.commandName}: failed to resolve web tool secrets locally (${formatErrorMessage(error)}).`,
);
}
}
@@ -650,7 +650,7 @@ async function resolveTargetSecretLocally(params: {
} catch (error) {
if (!enforcesResolvedSecrets(params.mode)) {
params.localResolutionDiagnostics.push(
`${params.commandName}: failed to resolve ${params.target.path} locally (${describeUnknownError(error)}).`,
`${params.commandName}: failed to resolve ${params.target.path} locally (${formatErrorMessage(error)}).`,
);
}
}
@@ -725,7 +725,7 @@ export async function resolveCommandSecretRefsViaGateway(params: {
resolvedConfig: fallback.resolvedConfig,
diagnostics: dedupeDiagnostics([
...fallback.diagnostics,
`${params.commandName}: gateway secrets.resolve unavailable (${describeUnknownError(err)}); ${fallbackMessage}`,
`${params.commandName}: gateway secrets.resolve unavailable (${formatErrorMessage(err)}); ${fallbackMessage}`,
]),
targetStatesByPath: fallback.targetStatesByPath,
hadUnresolvedTargets: fallback.hadUnresolvedTargets,
@@ -735,12 +735,12 @@ export async function resolveCommandSecretRefsViaGateway(params: {
}
if (isUnsupportedSecretsResolveError(err)) {
throw new Error(
`${params.commandName}: active gateway does not support secrets.resolve (${describeUnknownError(err)}). Update the gateway or run without SecretRefs.`,
`${params.commandName}: active gateway does not support secrets.resolve (${formatErrorMessage(err)}). Update the gateway or run without SecretRefs.`,
{ cause: err },
);
}
throw new Error(
`${params.commandName}: failed to resolve secrets from the active gateway snapshot (${describeUnknownError(err)}). Start the gateway and retry.`,
`${params.commandName}: failed to resolve secrets from the active gateway snapshot (${formatErrorMessage(err)}). Start the gateway and retry.`,
{ cause: err },
);
}
@@ -757,7 +757,7 @@ export async function resolveCommandSecretRefsViaGateway(params: {
} catch (err) {
const path = pathSegments.join(".");
throw new Error(
`${params.commandName}: failed to apply resolved secret assignment at ${path} (${describeUnknownError(err)}).`,
`${params.commandName}: failed to apply resolved secret assignment at ${path} (${formatErrorMessage(err)}).`,
{ cause: err },
);
}
@@ -837,7 +837,7 @@ export async function resolveCommandSecretRefsViaGateway(params: {
scrubUnresolvedAssignments(resolvedConfig, analyzed.unresolved);
diagnostics = dedupeDiagnostics([
...diagnostics,
`${params.commandName}: local fallback after incomplete gateway snapshot failed (${describeUnknownError(error)}).`,
`${params.commandName}: local fallback after incomplete gateway snapshot failed (${formatErrorMessage(error)}).`,
...buildUnresolvedDiagnostics(params.commandName, analyzed.unresolved, mode),
]);
}

View File

@@ -2,6 +2,7 @@ import fs from "node:fs/promises";
import type { Command } from "commander";
import JSON5 from "json5";
import { readBestEffortConfig, type OpenClawConfig } from "../config/config.js";
import { formatErrorMessage } from "../infra/errors.js";
import {
collectExecPolicyScopeSnapshots,
type ExecPolicyScopeSnapshot,
@@ -17,7 +18,6 @@ import { defaultRuntime } from "../runtime.js";
import { formatDocsLink } from "../terminal/links.js";
import { getTerminalTableWidth, renderTable } from "../terminal/table.js";
import { isRich, theme } from "../terminal/theme.js";
import { describeUnknownError } from "./gateway-cli/shared.js";
import { callGatewayFromCli } from "./gateway-rpc.js";
import { nodesCallOpts, resolveNodeId } from "./nodes-cli/rpc.js";
import type { NodesRpcOpts } from "./nodes-cli/types.js";
@@ -157,7 +157,7 @@ async function saveSnapshotTargeted(params: {
}
function formatCliError(err: unknown): string {
const msg = describeUnknownError(err);
const msg = formatErrorMessage(err);
return msg.includes("\n") ? msg.split("\n")[0] : msg;
}

View File

@@ -36,7 +36,6 @@ import { withProgress } from "../progress.js";
import { ensureDevGatewayConfig } from "./dev.js";
import { runGatewayLoop } from "./run-loop.js";
import {
describeUnknownError,
extractGatewayMiskeys,
maybeExplainGatewayServiceStop,
parsePort,
@@ -559,7 +558,7 @@ async function runGatewayCommand(opts: GatewayRunOpts) {
}
} catch (err) {
if (isGatewayLockError(err)) {
const errMessage = describeUnknownError(err);
const errMessage = formatErrorMessage(err);
defaultRuntime.error(
`Gateway failed to start: ${errMessage}\nIf the gateway is supervised, stop it with: ${formatCliCommand("openclaw gateway stop")}`,
);

View File

@@ -4,7 +4,6 @@ import {
resolveGatewayWindowsTaskName,
} from "../../daemon/constants.js";
import { resolveGatewayService } from "../../daemon/service.js";
import { formatErrorMessage } from "../../infra/errors.js";
import { defaultRuntime } from "../../runtime.js";
import { formatCliCommand } from "../command-format.js";
import { parsePort } from "../shared/parse-port.js";
@@ -21,10 +20,6 @@ export const toOptionString = (value: unknown): string | undefined => {
return undefined;
};
export function describeUnknownError(err: unknown): string {
return formatErrorMessage(err);
}
export function extractGatewayMiskeys(parsed: unknown): {
hasGatewayToken: boolean;
hasRemoteToken: boolean;

View File

@@ -9,6 +9,7 @@ import { normalizeProviderId } from "../agents/model-selection.js";
import { resolveStateDir, type OpenClawConfig } from "../config/config.js";
import { coerceSecretRef } from "../config/types.secrets.js";
import { resolveSecretInputRef, type SecretRef } from "../config/types.secrets.js";
import { formatErrorMessage } from "../infra/errors.js";
import { resolveConfigDir, resolveUserPath } from "../utils.js";
import { runTasksWithConcurrency } from "../utils/run-with-concurrency.js";
import { iterateAuthProfileCredentials } from "./auth-profiles-scan.js";
@@ -27,7 +28,6 @@ import {
isExpectedResolvedSecretValue,
} from "./secret-value.js";
import { isNonEmptyString, isRecord } from "./shared.js";
import { describeUnknownError } from "./shared.js";
import {
listAgentModelsJsonPaths,
listAuthProfileStorePaths,
@@ -568,7 +568,7 @@ async function collectUnresolvedRefFindings(params: {
severity: "error",
file: assignment.file,
jsonPath: assignment.path,
message: `Failed to resolve ${assignment.ref.source}:${assignment.ref.provider}:${assignment.ref.id} (${describeUnknownError(resolveErr)}).`,
message: `Failed to resolve ${assignment.ref.source}:${assignment.ref.provider}:${assignment.ref.id} (${formatErrorMessage(resolveErr)}).`,
provider: assignment.provider,
});
continue;

View File

@@ -9,6 +9,7 @@ import type {
SecretRef,
SecretRefSource,
} from "../config/types.secrets.js";
import { formatErrorMessage } from "../infra/errors.js";
import { inspectPathPermissions, safeStat } from "../security/audit-fs.js";
import { isPathInside } from "../security/scan-paths.js";
import { resolveUserPath } from "../utils.js";
@@ -21,12 +22,7 @@ import {
resolveDefaultSecretProviderAlias,
secretRefKey,
} from "./ref-contract.js";
import {
describeUnknownError,
isNonEmptyString,
isRecord,
normalizePositiveInt,
} from "./shared.js";
import { isNonEmptyString, isRecord, normalizePositiveInt } from "./shared.js";
const DEFAULT_PROVIDER_CONCURRENCY = 4;
const DEFAULT_MAX_REFS_PER_PROVIDER = 512;
@@ -140,7 +136,7 @@ function throwUnknownProviderResolutionError(params: {
throw providerResolutionError({
source: params.source,
provider: params.provider,
message: describeUnknownError(params.err),
message: formatErrorMessage(params.err),
cause: params.err,
});
}
@@ -419,7 +415,7 @@ async function resolveFileRefs(params: {
source: "file",
provider: params.providerName,
refId: ref.id,
message: describeUnknownError(err),
message: formatErrorMessage(err),
cause: err,
});
}

View File

@@ -1,6 +1,5 @@
import fs from "node:fs";
import path from "node:path";
import { formatErrorMessage } from "../infra/errors.js";
export { isRecord } from "../utils.js";
export function isNonEmptyString(value: unknown): value is string {
@@ -60,7 +59,3 @@ export function writeTextFileAtomic(pathname: string, value: string, mode = 0o60
fs.chmodSync(tempPath, mode);
fs.renameSync(tempPath, pathname);
}
export function describeUnknownError(err: unknown): string {
return formatErrorMessage(err);
}