Skip to content

Commit 3d23103

Browse files
committed
refactor: dedupe hook gateway error formatting
1 parent 474db91 commit 3d23103

16 files changed

Lines changed: 43 additions & 38 deletions

src/acp/control-plane/manager.core.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { resolveAgentTimeoutMs } from "../../agents/timeout.js";
22
import type { OpenClawConfig } from "../../config/config.js";
33
import { logVerbose } from "../../globals.js";
4+
import { formatErrorMessage } from "../../infra/errors.js";
45
import { normalizeAgentId } from "../../routing/session-key.js";
56
import { isAcpSessionKey } from "../../sessions/session-key-utils.js";
67
import {
@@ -1291,7 +1292,7 @@ export class AcpSessionManager {
12911292
});
12921293
} catch (recoveryError) {
12931294
logVerbose(
1294-
`acp close recovery: unable to prepare fresh session for ${sessionKey}: ${recoveryError instanceof Error ? recoveryError.message : String(recoveryError)}`,
1295+
`acp close recovery: unable to prepare fresh session for ${sessionKey}: ${formatErrorMessage(recoveryError)}`,
12951296
);
12961297
}
12971298
}
@@ -1665,7 +1666,7 @@ export class AcpSessionManager {
16651666
});
16661667
} catch (error) {
16671668
logVerbose(
1668-
`acp-manager: failed preparing a fresh persistent session for ${params.sessionKey}: ${error instanceof Error ? error.message : String(error)}`,
1669+
`acp-manager: failed preparing a fresh persistent session for ${params.sessionKey}: ${formatErrorMessage(error)}`,
16691670
);
16701671
return false;
16711672
}

src/acp/persistent-bindings.lifecycle.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { OpenClawConfig } from "../config/config.js";
22
import type { SessionAcpMeta } from "../config/sessions/types.js";
33
import { logVerbose } from "../globals.js";
4+
import { formatErrorMessage } from "../infra/errors.js";
45
import { getAcpSessionManager } from "./control-plane/manager.js";
56
import { resolveConfiguredAcpBindingSpecBySessionKey } from "./persistent-bindings.resolve.js";
67
import {
@@ -98,7 +99,7 @@ export async function ensureConfiguredAcpBindingSession(params: {
9899
sessionKey,
99100
};
100101
} catch (error) {
101-
const message = error instanceof Error ? error.message : String(error);
102+
const message = formatErrorMessage(error);
102103
logVerbose(
103104
`acp-configured-binding: failed ensuring ${params.spec.channel}:${params.spec.accountId}:${params.spec.conversationId} -> ${sessionKey}: ${message}`,
104105
);
@@ -192,7 +193,7 @@ export async function resetAcpSessionInPlace(params: {
192193
// on the next turn through the normal binding readiness path.
193194
return { ok: true };
194195
} catch (error) {
195-
const message = error instanceof Error ? error.message : String(error);
196+
const message = formatErrorMessage(error);
196197
logVerbose(`acp-configured-binding: failed reset for ${sessionKey}: ${message}`);
197198
return {
198199
ok: false,

src/config/io.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import JSON5 from "json5";
77
import { ensureOwnerDisplaySecret } from "../agents/owner-display.js";
88
import { applyRuntimeLegacyConfigMigrations } from "../commands/doctor/shared/runtime-compat-api.js";
99
import { loadDotEnv } from "../infra/dotenv.js";
10+
import { formatErrorMessage } from "../infra/errors.js";
1011
import { resolveRequiredHomeDir } from "../infra/home-dir.js";
1112
import {
1213
loadShellEnvFallback,
@@ -2550,7 +2551,7 @@ export async function writeConfigFile(
25502551
} catch {
25512552
// Keep the original refresh failure as the surfaced error.
25522553
}
2553-
const detail = error instanceof Error ? error.message : String(error);
2554+
const detail = formatErrorMessage(error);
25542555
throw new ConfigRuntimeRefreshError(
25552556
`Config was written to ${io.configPath}, but runtime snapshot refresh failed: ${detail}`,
25562557
{ cause: error },

src/config/sessions/transcript.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import fs from "node:fs";
22
import path from "node:path";
33
import { CURRENT_SESSION_VERSION, SessionManager } from "@mariozechner/pi-coding-agent";
4+
import { formatErrorMessage } from "../../infra/errors.js";
45
import { emitSessionTranscriptUpdate } from "../../sessions/transcript-events.js";
56
import {
67
resolveDefaultSessionStorePath,
@@ -184,7 +185,7 @@ export async function appendExactAssistantMessageToSessionTranscript(params: {
184185
} catch (err) {
185186
return {
186187
ok: false,
187-
reason: err instanceof Error ? err.message : String(err),
188+
reason: formatErrorMessage(err),
188189
};
189190
}
190191

src/gateway/boot.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
import { resolveStorePath } from "../config/sessions/paths.js";
1414
import { loadSessionStore, updateSessionStore } from "../config/sessions/store.js";
1515
import type { SessionEntry } from "../config/sessions/types.js";
16+
import { formatErrorMessage } from "../infra/errors.js";
1617
import { createSubsystemLogger } from "../logging/subsystem.js";
1718
import { type RuntimeEnv, defaultRuntime } from "../runtime.js";
1819

@@ -131,7 +132,7 @@ async function restoreMainSessionMapping(
131132
);
132133
return undefined;
133134
} catch (err) {
134-
return err instanceof Error ? err.message : String(err);
135+
return formatErrorMessage(err);
135136
}
136137
}
137138

@@ -150,7 +151,7 @@ export async function runBootOnce(params: {
150151
try {
151152
result = await loadBootFile(params.workspaceDir);
152153
} catch (err) {
153-
const message = err instanceof Error ? err.message : String(err);
154+
const message = formatErrorMessage(err);
154155
log.error(`boot: failed to read ${BOOT_FILENAME}: ${message}`);
155156
return { status: "failed", reason: message };
156157
}
@@ -183,7 +184,7 @@ export async function runBootOnce(params: {
183184
params.deps,
184185
);
185186
} catch (err) {
186-
agentFailure = err instanceof Error ? err.message : String(err);
187+
agentFailure = formatErrorMessage(err);
187188
log.error(`boot: agent run failed: ${agentFailure}`);
188189
}
189190

src/gateway/chat-attachments.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { formatErrorMessage } from "../infra/errors.js";
12
import { estimateBase64DecodedBytes } from "../media/base64.js";
23
import type { PromptImageOrderEntry } from "../media/prompt-image-order.js";
34
import { sniffMimeFromBase64 } from "../media/sniff-mime-from-base64.js";
@@ -428,7 +429,7 @@ export async function parseMessageWithAttachments(
428429

429430
isOffloaded = true;
430431
} catch (err) {
431-
const errorMessage = err instanceof Error ? err.message : String(err);
432+
const errorMessage = formatErrorMessage(err);
432433
throw new MediaOffloadError(
433434
`[Gateway Error] Failed to save intercepted media to disk: ${errorMessage}`,
434435
{ cause: err },

src/gateway/exec-approval-ios-push.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
type DeviceAuthToken,
66
type PairedDevice,
77
} from "../infra/device-pairing.js";
8+
import { formatErrorMessage } from "../infra/errors.js";
89
import type { ExecApprovalRequest, ExecApprovalResolved } from "../infra/exec-approvals.js";
910
import {
1011
clearApnsRegistrationIfCurrent,
@@ -208,8 +209,7 @@ async function sendRequestedPushes(params: {
208209
);
209210
for (const result of results) {
210211
if (result.status === "rejected") {
211-
const message =
212-
result.reason instanceof Error ? result.reason.message : String(result.reason);
212+
const message = formatErrorMessage(result.reason);
213213
params.log.warn?.(`exec approvals: iOS request push threw error: ${message}`);
214214
}
215215
}
@@ -274,7 +274,7 @@ export function createExecApprovalIosPushDelivery(params: { log: GatewayLikeLogg
274274
nodeIds: plan.targets.map((target) => target.nodeId),
275275
requestPushPromise: sendRequestedPushes({ request, plan, log: params.log }).catch(
276276
(err) => {
277-
const message = err instanceof Error ? err.message : String(err);
277+
const message = formatErrorMessage(err);
278278
params.log.error?.(`exec approvals: iOS request push failed: ${message}`);
279279
return { attempted: plan.targets.length, delivered: 0 };
280280
},

src/gateway/mcp-http.handlers.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import crypto from "node:crypto";
2+
import { formatErrorMessage } from "../infra/errors.js";
23
import {
34
MCP_LOOPBACK_SERVER_NAME,
45
MCP_LOOPBACK_SERVER_VERSION,
@@ -75,7 +76,7 @@ export async function handleMcpJsonRpc(params: {
7576
isError: false,
7677
});
7778
} catch (error) {
78-
const message = error instanceof Error ? error.message : String(error);
79+
const message = formatErrorMessage(error);
7980
return jsonRpcResult(id, {
8081
content: [{ type: "text", text: message || "tool execution failed" }],
8182
isError: true,

src/gateway/mcp-http.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import crypto from "node:crypto";
22
import { createServer as createHttpServer } from "node:http";
33
import { loadConfig } from "../config/config.js";
4+
import { formatErrorMessage } from "../infra/errors.js";
45
import { logDebug, logWarn } from "../logger.js";
56
import { handleMcpJsonRpc } from "./mcp-http.handlers.js";
67
import {
@@ -72,9 +73,7 @@ export async function startMcpLoopbackServer(port = 0): Promise<{
7273
res.writeHead(200, { "Content-Type": "application/json" });
7374
res.end(payload);
7475
} catch (error) {
75-
logWarn(
76-
`mcp loopback: request handling failed: ${error instanceof Error ? error.message : String(error)}`,
77-
);
76+
logWarn(`mcp loopback: request handling failed: ${formatErrorMessage(error)}`);
7877
if (!res.headersSent) {
7978
res.writeHead(400, { "Content-Type": "application/json" });
8079
res.end(JSON.stringify(jsonRpcError(null, -32700, "Parse error")));

src/gateway/server-node-events.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { randomUUID } from "node:crypto";
2+
import { formatErrorMessage } from "../infra/errors.js";
23
import type { PromptImageOrderEntry } from "../media/prompt-image-order.js";
34
import type { NodeEvent, NodeEventContext } from "./server-node-events-types.js";
45
import {
@@ -408,17 +409,15 @@ export const handleNodeEvent = async (ctx: NodeEventContext, nodeId: string, evt
408409
await deleteMediaBuffer(ref.id);
409410
} catch (cleanupErr) {
410411
ctx.logGateway.warn(
411-
`Failed to cleanup orphaned media ${ref.id}: ${cleanupErr instanceof Error ? cleanupErr.message : String(cleanupErr)}`,
412+
`Failed to cleanup orphaned media ${ref.id}: ${formatErrorMessage(cleanupErr)}`,
412413
);
413414
}
414415
}
415416
}
416417
return;
417418
}
418419
} catch (err) {
419-
ctx.logGateway.warn(
420-
`agent.request attachment parse failed: ${err instanceof Error ? err.message : String(err)}`,
421-
);
420+
ctx.logGateway.warn(`agent.request attachment parse failed: ${formatErrorMessage(err)}`);
422421
return;
423422
}
424423
}

0 commit comments

Comments
 (0)