From 54cd8ed25be6b9cbfa4cae39a4f247c4a865601f Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 7 Apr 2026 03:48:34 +0100 Subject: [PATCH] refactor: dedupe extension error formatting --- extensions/amazon-bedrock-mantle/discovery.ts | 5 ++-- extensions/discord/src/monitor/provider.ts | 2 +- extensions/elevenlabs/speech-provider.ts | 3 ++- extensions/feishu/src/bitable.ts | 3 ++- extensions/feishu/src/chat.ts | 3 ++- extensions/feishu/src/docx.ts | 7 +++--- extensions/feishu/src/drive.ts | 3 ++- extensions/feishu/src/monitor.comment.ts | 3 ++- extensions/feishu/src/probe.ts | 3 ++- extensions/feishu/src/tool-result.ts | 4 ++- extensions/lobster/src/lobster-runner.ts | 25 ++++++------------- extensions/speech-core/src/tts.ts | 3 ++- .../telegram/src/bot-message-dispatch.ts | 9 +++---- extensions/telegram/src/doctor.ts | 7 ++---- extensions/telegram/src/thread-bindings.ts | 3 ++- extensions/tlon/index.ts | 3 ++- extensions/tlon/src/monitor/media.ts | 5 ++-- extensions/zalo/src/send.ts | 3 ++- extensions/zalouser/src/probe.ts | 3 ++- extensions/zalouser/src/tool.ts | 3 ++- 20 files changed, 49 insertions(+), 51 deletions(-) diff --git a/extensions/amazon-bedrock-mantle/discovery.ts b/extensions/amazon-bedrock-mantle/discovery.ts index abea424e568..144854718cc 100644 --- a/extensions/amazon-bedrock-mantle/discovery.ts +++ b/extensions/amazon-bedrock-mantle/discovery.ts @@ -1,4 +1,5 @@ import { createSubsystemLogger } from "openclaw/plugin-sdk/core"; +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import type { ModelDefinitionConfig, ModelProviderConfig, @@ -102,7 +103,7 @@ export async function generateBearerTokenFromIam(params: { } catch (error) { log.debug?.("Mantle IAM token generation unavailable", { region: params.region, - error: error instanceof Error ? error.message : String(error), + error: formatErrorMessage(error), }); return undefined; } @@ -233,7 +234,7 @@ export async function discoverMantleModels(params: { return models; } catch (error) { log.debug?.("Mantle model discovery error", { - error: error instanceof Error ? error.message : String(error), + error: formatErrorMessage(error), }); return cached?.models ?? []; } diff --git a/extensions/discord/src/monitor/provider.ts b/extensions/discord/src/monitor/provider.ts index 99bb61bc171..55d1e1ffb21 100644 --- a/extensions/discord/src/monitor/provider.ts +++ b/extensions/discord/src/monitor/provider.ts @@ -223,7 +223,7 @@ function classifyAcpStatusProbeError(params: { return { status: "stale", reason: "session-init-failed" }; } - const message = params.error instanceof Error ? params.error.message : String(params.error); + const message = formatErrorMessage(params.error); if (isLegacyMissingSessionError(message)) { return { status: "stale", reason: "session-missing" }; } diff --git a/extensions/elevenlabs/speech-provider.ts b/extensions/elevenlabs/speech-provider.ts index e8184eac4da..75dea4c2da9 100644 --- a/extensions/elevenlabs/speech-provider.ts +++ b/extensions/elevenlabs/speech-provider.ts @@ -1,3 +1,4 @@ +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import { normalizeResolvedSecretInputString } from "openclaw/plugin-sdk/secret-input"; import type { SpeechDirectiveTokenParseContext, @@ -281,7 +282,7 @@ function parseDirectiveToken(ctx: SpeechDirectiveTokenParseContext) { } catch (error) { return { handled: true, - warnings: [error instanceof Error ? error.message : String(error)], + warnings: [formatErrorMessage(error)], }; } } diff --git a/extensions/feishu/src/bitable.ts b/extensions/feishu/src/bitable.ts index 9ed389aec39..ae166b2802f 100644 --- a/extensions/feishu/src/bitable.ts +++ b/extensions/feishu/src/bitable.ts @@ -1,5 +1,6 @@ import type * as Lark from "@larksuiteoapi/node-sdk"; import { Type } from "@sinclair/typebox"; +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import type { OpenClawPluginApi } from "../runtime-api.js"; import { listEnabledFeishuAccounts } from "./accounts.js"; import { createFeishuToolClient } from "./tool-account.js"; @@ -579,7 +580,7 @@ export function registerFeishuBitableTools(api: OpenClawPluginApi) { }), ); } catch (err) { - return json({ error: err instanceof Error ? err.message : String(err) }); + return json({ error: formatErrorMessage(err) }); } }, }), diff --git a/extensions/feishu/src/chat.ts b/extensions/feishu/src/chat.ts index b32dfb41230..39e345c820d 100644 --- a/extensions/feishu/src/chat.ts +++ b/extensions/feishu/src/chat.ts @@ -1,4 +1,5 @@ import type * as Lark from "@larksuiteoapi/node-sdk"; +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import type { OpenClawPluginApi } from "../runtime-api.js"; import { listEnabledFeishuAccounts } from "./accounts.js"; import { FeishuChatSchema, type FeishuChatParams } from "./chat-schema.js"; @@ -181,7 +182,7 @@ export function registerFeishuChatTools(api: OpenClawPluginApi) { return json({ error: `Unknown action: ${String(p.action)}` }); } } catch (err) { - return json({ error: err instanceof Error ? err.message : String(err) }); + return json({ error: formatErrorMessage(err) }); } }, }, diff --git a/extensions/feishu/src/docx.ts b/extensions/feishu/src/docx.ts index cc8d99b4e73..7f4a8b6652d 100644 --- a/extensions/feishu/src/docx.ts +++ b/extensions/feishu/src/docx.ts @@ -4,6 +4,7 @@ import { isAbsolute, resolve } from "node:path"; import { basename } from "node:path"; import type * as Lark from "@larksuiteoapi/node-sdk"; import { Type } from "@sinclair/typebox"; +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import type { OpenClawPluginApi } from "../runtime-api.js"; import { listEnabledFeishuAccounts } from "./accounts.js"; import { FeishuDocSchema, type FeishuDocParams } from "./doc-schema.js"; @@ -916,7 +917,7 @@ async function createDoc( }); requesterPermissionAdded = true; } catch (err) { - requesterPermissionError = err instanceof Error ? err.message : String(err); + requesterPermissionError = formatErrorMessage(err); } } } @@ -1549,7 +1550,7 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) { return json({ error: "Unknown action" }); } } catch (err) { - return json({ error: err instanceof Error ? err.message : String(err) }); + return json({ error: formatErrorMessage(err) }); } }, }; @@ -1573,7 +1574,7 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) { const result = await listAppScopes(getClient(undefined, ctx.agentAccountId)); return json(result); } catch (err) { - return json({ error: err instanceof Error ? err.message : String(err) }); + return json({ error: formatErrorMessage(err) }); } }, }), diff --git a/extensions/feishu/src/drive.ts b/extensions/feishu/src/drive.ts index 02df66237ff..24f8253adba 100644 --- a/extensions/feishu/src/drive.ts +++ b/extensions/feishu/src/drive.ts @@ -1,4 +1,5 @@ import type * as Lark from "@larksuiteoapi/node-sdk"; +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import type { OpenClawPluginApi } from "../runtime-api.js"; import { listEnabledFeishuAccounts } from "./accounts.js"; import { encodeQuery, extractReplyText, isRecord, readString } from "./comment-shared.js"; @@ -687,7 +688,7 @@ export async function deliverCommentThreadText( console.warn( `[feishu_drive] comment metadata preflight failed ` + `comment=${params.comment_id} file_type=${params.file_type} ` + - `error=${error instanceof Error ? error.message : String(error)}`, + `error=${formatErrorMessage(error)}`, ); isWholeComment = false; } diff --git a/extensions/feishu/src/monitor.comment.ts b/extensions/feishu/src/monitor.comment.ts index 4e77531f673..8dc074bd63e 100644 --- a/extensions/feishu/src/monitor.comment.ts +++ b/extensions/feishu/src/monitor.comment.ts @@ -1,3 +1,4 @@ +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import type { ClawdbotConfig } from "../runtime-api.js"; import { resolveFeishuAccount } from "./accounts.js"; import { raceWithTimeoutAndAbort } from "./async.js"; @@ -126,7 +127,7 @@ function safeJsonStringify(value: unknown): string { return JSON.stringify(value); } catch (error) { return JSON.stringify({ - error: error instanceof Error ? error.message : String(error), + error: formatErrorMessage(error), }); } } diff --git a/extensions/feishu/src/probe.ts b/extensions/feishu/src/probe.ts index 0fcbc5f14e8..7f55300889d 100644 --- a/extensions/feishu/src/probe.ts +++ b/extensions/feishu/src/probe.ts @@ -1,3 +1,4 @@ +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import { raceWithTimeoutAndAbort } from "./async.js"; import { createFeishuClient, type FeishuClientCredentials } from "./client.js"; import type { FeishuProbeResult } from "./types.js"; @@ -151,7 +152,7 @@ export async function probeFeishu( { ok: false, appId: creds.appId, - error: err instanceof Error ? err.message : String(err), + error: formatErrorMessage(err), }, PROBE_ERROR_TTL_MS, ); diff --git a/extensions/feishu/src/tool-result.ts b/extensions/feishu/src/tool-result.ts index d45bb0cf1c0..6a0cb6270e7 100644 --- a/extensions/feishu/src/tool-result.ts +++ b/extensions/feishu/src/tool-result.ts @@ -1,3 +1,5 @@ +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; + export function jsonToolResult(data: unknown) { return { content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }], @@ -10,5 +12,5 @@ export function unknownToolActionResult(action: unknown) { } export function toolExecutionErrorResult(error: unknown) { - return jsonToolResult({ error: error instanceof Error ? error.message : String(error) }); + return jsonToolResult({ error: formatErrorMessage(error) }); } diff --git a/extensions/lobster/src/lobster-runner.ts b/extensions/lobster/src/lobster-runner.ts index 435287fcf05..8455913bc01 100644 --- a/extensions/lobster/src/lobster-runner.ts +++ b/extensions/lobster/src/lobster-runner.ts @@ -4,6 +4,7 @@ import { createRequire } from "node:module"; import path from "node:path"; import { Readable, Writable } from "node:stream"; import { pathToFileURL } from "node:url"; +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; export type LobsterEnvelope = | { @@ -460,10 +461,7 @@ function createFallbackEmbeddedToolRuntime(deps: ToolRuntimeDeps): EmbeddedToolR }); return normalizeWorkflowOutput(okEnvelope, output); } catch (error) { - return errorEnvelope( - "runtime_error", - error instanceof Error ? error.message : String(error), - ); + return errorEnvelope("runtime_error", formatErrorMessage(error)); } } @@ -471,7 +469,7 @@ function createFallbackEmbeddedToolRuntime(deps: ToolRuntimeDeps): EmbeddedToolR try { parsed = deps.parsePipeline(String(pipeline)); } catch (error) { - return errorEnvelope("parse_error", error instanceof Error ? error.message : String(error)); + return errorEnvelope("parse_error", formatErrorMessage(error)); } try { @@ -512,10 +510,7 @@ function createFallbackEmbeddedToolRuntime(deps: ToolRuntimeDeps): EmbeddedToolR return okEnvelope("ok", output.items, null); } catch (error) { - return errorEnvelope( - "runtime_error", - error instanceof Error ? error.message : String(error), - ); + return errorEnvelope("runtime_error", formatErrorMessage(error)); } }, @@ -526,7 +521,7 @@ function createFallbackEmbeddedToolRuntime(deps: ToolRuntimeDeps): EmbeddedToolR try { payload = deps.decodeResumeToken(token); } catch (error) { - return errorEnvelope("parse_error", error instanceof Error ? error.message : String(error)); + return errorEnvelope("parse_error", formatErrorMessage(error)); } if (!approved) { @@ -549,10 +544,7 @@ function createFallbackEmbeddedToolRuntime(deps: ToolRuntimeDeps): EmbeddedToolR }); return normalizeWorkflowOutput(okEnvelope, output); } catch (error) { - return errorEnvelope( - "runtime_error", - error instanceof Error ? error.message : String(error), - ); + return errorEnvelope("runtime_error", formatErrorMessage(error)); } } @@ -603,10 +595,7 @@ function createFallbackEmbeddedToolRuntime(deps: ToolRuntimeDeps): EmbeddedToolR } return okEnvelope("ok", output.items, null); } catch (error) { - return errorEnvelope( - "runtime_error", - error instanceof Error ? error.message : String(error), - ); + return errorEnvelope("runtime_error", formatErrorMessage(error)); } }, }; diff --git a/extensions/speech-core/src/tts.ts b/extensions/speech-core/src/tts.ts index e307974be57..35c4fb0e3d5 100644 --- a/extensions/speech-core/src/tts.ts +++ b/extensions/speech-core/src/tts.ts @@ -18,6 +18,7 @@ import type { TtsModelOverrideConfig, TtsProvider, } from "openclaw/plugin-sdk/config-runtime"; +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import { redactSensitiveText } from "openclaw/plugin-sdk/logging-core"; import { resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload"; import type { ReplyPayload } from "openclaw/plugin-sdk/reply-runtime"; @@ -569,7 +570,7 @@ function formatTtsProviderError(provider: TtsProvider, err: unknown): string { } function sanitizeTtsErrorForLog(err: unknown): string { - const raw = err instanceof Error ? err.message : String(err); + const raw = formatErrorMessage(err); return redactSensitiveText(raw).replace(/\r/g, "\\r").replace(/\n/g, "\\n").replace(/\t/g, "\\t"); } diff --git a/extensions/telegram/src/bot-message-dispatch.ts b/extensions/telegram/src/bot-message-dispatch.ts index 750bde2865b..8abe1ece794 100644 --- a/extensions/telegram/src/bot-message-dispatch.ts +++ b/extensions/telegram/src/bot-message-dispatch.ts @@ -12,6 +12,7 @@ import type { TelegramAccountConfig, TelegramDirectConfig, } from "openclaw/plugin-sdk/config-runtime"; +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import { clearHistoryEntriesIfEnabled } from "openclaw/plugin-sdk/reply-history"; import { resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload"; import type { ReplyPayload } from "openclaw/plugin-sdk/reply-runtime"; @@ -568,9 +569,7 @@ export const dispatchTelegramMessage = async ({ logVerbose("auto-topic-label: SessionKey is absent, skipping first-turn detection"); } } catch (err) { - logVerbose( - `auto-topic-label: session store error: ${err instanceof Error ? err.message : String(err)}`, - ); + logVerbose(`auto-topic-label: session store error: ${formatErrorMessage(err)}`); } } @@ -957,9 +956,7 @@ export const dispatchTelegramMessage = async ({ await bot.api.editForumTopic(chatId, topicThreadId, { name: label }); logVerbose(`auto-topic-label: renamed topic ${chatId}/${topicThreadId}`); } catch (err) { - logVerbose( - `auto-topic-label: failed: ${err instanceof Error ? err.message : String(err)}`, - ); + logVerbose(`auto-topic-label: failed: ${formatErrorMessage(err)}`); } })(); } diff --git a/extensions/telegram/src/doctor.ts b/extensions/telegram/src/doctor.ts index 1f994589bb8..e61b6ecdcba 100644 --- a/extensions/telegram/src/doctor.ts +++ b/extensions/telegram/src/doctor.ts @@ -3,6 +3,7 @@ import { type ChannelDoctorEmptyAllowlistAccountContext, } from "openclaw/plugin-sdk/channel-contract"; import { type OpenClawConfig } from "openclaw/plugin-sdk/config-runtime"; +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import { inspectTelegramAccount } from "./account-inspect.js"; import { listTelegramAccountIds, resolveTelegramAccount } from "./accounts.js"; import { isNumericTelegramUserId, normalizeTelegramAllowFromEntry } from "./allow-from.js"; @@ -32,10 +33,6 @@ function sanitizeForLog(value: string): string { return value.replace(/\p{Cc}+/gu, " ").trim(); } -function describeUnknownError(error: unknown): string { - return error instanceof Error ? error.message : String(error); -} - function hasAllowFromEntries(values?: DoctorAllowFromList): boolean { return Array.isArray(values) && values.some((entry) => String(entry).trim()); } @@ -170,7 +167,7 @@ export async function maybeRepairTelegramAllowFromUsernames(cfg: OpenClawConfig) inspected = inspectTelegramAccount({ cfg: resolvedConfig, accountId }); } catch (error) { tokenResolutionWarnings.push( - `- Telegram account ${accountId}: failed to inspect bot token (${describeUnknownError(error)}).`, + `- Telegram account ${accountId}: failed to inspect bot token (${formatErrorMessage(error)}).`, ); continue; } diff --git a/extensions/telegram/src/thread-bindings.ts b/extensions/telegram/src/thread-bindings.ts index dd059a9b2a7..e49a31bc9f5 100644 --- a/extensions/telegram/src/thread-bindings.ts +++ b/extensions/telegram/src/thread-bindings.ts @@ -3,6 +3,7 @@ import os from "node:os"; import path from "node:path"; import { loadConfig } from "openclaw/plugin-sdk/config-runtime"; import { + formatErrorMessage, formatThreadBindingDurationLabel, registerSessionBindingAdapter, resolveThreadBindingConversationIdFromBindingId, @@ -607,7 +608,7 @@ export function createTelegramThreadBindingManager( conversationId = `${result.chatId}:topic:${result.topicId}`; } catch (err) { logVerbose( - `telegram: child thread-binding failed for ${chatId}: ${err instanceof Error ? err.message : String(err)}`, + `telegram: child thread-binding failed for ${chatId}: ${formatErrorMessage(err)}`, ); return null; } diff --git a/extensions/tlon/index.ts b/extensions/tlon/index.ts index 06ebc2aa47a..fd2ec8112e2 100644 --- a/extensions/tlon/index.ts +++ b/extensions/tlon/index.ts @@ -3,6 +3,7 @@ import { existsSync } from "node:fs"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; import { defineBundledChannelEntry } from "openclaw/plugin-sdk/channel-entry-contract"; +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; const __dirname = dirname(fileURLToPath(import.meta.url)); @@ -171,7 +172,7 @@ export default defineBundledChannelEntry({ content: [ { type: "text" as const, - text: `Error: ${error instanceof Error ? error.message : String(error)}`, + text: `Error: ${formatErrorMessage(error)}`, }, ], details: { error: true }, diff --git a/extensions/tlon/src/monitor/media.ts b/extensions/tlon/src/monitor/media.ts index 182ef259846..0035760e409 100644 --- a/extensions/tlon/src/monitor/media.ts +++ b/extensions/tlon/src/monitor/media.ts @@ -1,6 +1,7 @@ import { randomUUID } from "node:crypto"; import { mkdir, writeFile } from "node:fs/promises"; import * as path from "node:path"; +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import { fetchRemoteMedia, MAX_IMAGE_BYTES, @@ -102,9 +103,7 @@ export async function downloadMedia( originalUrl: url, }; } catch (error: unknown) { - console.error( - `[tlon-media] Error downloading ${url}: ${error instanceof Error ? error.message : String(error)}`, - ); + console.error(`[tlon-media] Error downloading ${url}: ${formatErrorMessage(error)}`); return null; } } diff --git a/extensions/zalo/src/send.ts b/extensions/zalo/src/send.ts index 647ca3b9823..cc9ef0d2d85 100644 --- a/extensions/zalo/src/send.ts +++ b/extensions/zalo/src/send.ts @@ -1,3 +1,4 @@ +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import { resolveZaloAccount } from "./accounts.js"; import type { ZaloFetch } from "./api.js"; import { sendMessage, sendPhoto } from "./api.js"; @@ -39,7 +40,7 @@ async function runZaloSend( const result = toZaloSendResult(await send()); return result.ok ? result : { ok: false, error: failureMessage }; } catch (err) { - return { ok: false, error: err instanceof Error ? err.message : String(err) }; + return { ok: false, error: formatErrorMessage(err) }; } } diff --git a/extensions/zalouser/src/probe.ts b/extensions/zalouser/src/probe.ts index bb3daaabbb3..9c283645156 100644 --- a/extensions/zalouser/src/probe.ts +++ b/extensions/zalouser/src/probe.ts @@ -1,3 +1,4 @@ +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import type { BaseProbeResult } from "../runtime-api.js"; import type { ZcaUserInfo } from "./types.js"; import { getZaloUserInfo } from "./zalo-js.js"; @@ -28,7 +29,7 @@ export async function probeZalouser( } catch (error) { return { ok: false, - error: error instanceof Error ? error.message : String(error), + error: formatErrorMessage(error), }; } } diff --git a/extensions/zalouser/src/tool.ts b/extensions/zalouser/src/tool.ts index 0c76a355e96..4886002a3d3 100644 --- a/extensions/zalouser/src/tool.ts +++ b/extensions/zalouser/src/tool.ts @@ -1,4 +1,5 @@ import { Type } from "@sinclair/typebox"; +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import type { AnyAgentTool, OpenClawPluginToolContext } from "../runtime-api.js"; import { sendImageZalouser, sendLinkZalouser, sendMessageZalouser } from "./send.js"; import { parseZalouserOutboundTarget } from "./session-route.js"; @@ -189,7 +190,7 @@ export async function executeZalouserTool( } } catch (err) { return json({ - error: err instanceof Error ? err.message : String(err), + error: formatErrorMessage(err), }); } }