From 60a8ec821cb8d71273dc4cda7e78ebd8fd093de2 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 4 Aug 2026 21:05:45 -0700 Subject: [PATCH] refactor(plugins): reuse canonical error coercion (#119451) --- extensions/acpx/src/runtime-turn.ts | 17 ++----------- extensions/copilot/src/event-bridge.ts | 17 ++----------- extensions/discord/src/monitor.gateway.ts | 17 ++----------- extensions/line/src/bot-handlers.ts | 17 ++----------- extensions/memory-core/src/memory/manager.ts | 25 +++++++------------- extensions/qa-channel/src/bus-client.ts | 17 ++----------- extensions/whatsapp/src/session.ts | 20 ++-------------- 7 files changed, 20 insertions(+), 110 deletions(-) diff --git a/extensions/acpx/src/runtime-turn.ts b/extensions/acpx/src/runtime-turn.ts index ff51c37484c1..cec67e20b51b 100644 --- a/extensions/acpx/src/runtime-turn.ts +++ b/extensions/acpx/src/runtime-turn.ts @@ -2,6 +2,7 @@ * ACPX turn adapters. Modern runtimes can expose startTurn directly; legacy * runtimes that only stream runTurn events are adapted to the newer contract. */ +import { toErrorObject } from "openclaw/plugin-sdk/error-runtime"; import { createDeferred } from "openclaw/plugin-sdk/extension-shared"; import type { AcpRuntime, @@ -67,7 +68,7 @@ class LegacyRunTurnEventQueue { return item; } if (this.error) { - throw toLintErrorObject(this.error, "Non-Error thrown"); + throw toErrorObject(this.error, "Non-Error thrown"); } if (this.closed) { return null; @@ -183,17 +184,3 @@ export function lazyStartRuntimeTurn( }, }; } - -function toLintErrorObject(value: unknown, fallbackMessage: string): Error { - if (value instanceof Error) { - return value; - } - if (typeof value === "string") { - return new Error(value); - } - const error = new Error(fallbackMessage, { cause: value }); - if ((typeof value === "object" && value !== null) || typeof value === "function") { - Object.assign(error, value); - } - return error; -} diff --git a/extensions/copilot/src/event-bridge.ts b/extensions/copilot/src/event-bridge.ts index 3d239215bcf0..e6f58385777f 100644 --- a/extensions/copilot/src/event-bridge.ts +++ b/extensions/copilot/src/event-bridge.ts @@ -4,6 +4,7 @@ import type { AgentHarnessAttemptResult, AgentMessage, } from "openclaw/plugin-sdk/agent-harness-runtime"; +import { toErrorObject } from "openclaw/plugin-sdk/error-runtime"; import { buildAssistantMessage, hasOwnKeys, @@ -254,7 +255,7 @@ export function attachEventBridge( }); deltaChain = deltaQueue.then(() => { if (firstDeltaError !== undefined) { - throw toLintErrorObject(firstDeltaError, "Non-Error thrown"); + throw toErrorObject(firstDeltaError, "Non-Error thrown"); } }); void deltaChain.catch(() => undefined); @@ -986,17 +987,3 @@ function registerListener( session.off?.(eventType, handler as (...args: unknown[]) => void); }); } - -function toLintErrorObject(value: unknown, fallbackMessage: string): Error { - if (value instanceof Error) { - return value; - } - if (typeof value === "string") { - return new Error(value); - } - const error = new Error(fallbackMessage, { cause: value }); - if ((typeof value === "object" && value !== null) || typeof value === "function") { - Object.assign(error, value); - } - return error; -} diff --git a/extensions/discord/src/monitor.gateway.ts b/extensions/discord/src/monitor.gateway.ts index 1ad201a0f1f4..1c2eaa5c1671 100644 --- a/extensions/discord/src/monitor.gateway.ts +++ b/extensions/discord/src/monitor.gateway.ts @@ -1,4 +1,5 @@ // Discord plugin module implements monitor.gateway behavior. +import { toErrorObject } from "openclaw/plugin-sdk/error-runtime"; import type { DiscordGatewayHandle } from "./monitor/gateway-handle.js"; import { DiscordGatewayLifecycleError } from "./monitor/gateway-supervisor.js"; import type { @@ -49,7 +50,7 @@ export async function waitForDiscordGatewayStop( gateway?.disconnect?.(); } finally { cleanup(); - reject(toLintErrorObject(err, "Non-Error rejection")); + reject(toErrorObject(err, "Non-Error rejection")); } }; const onAbort = () => { @@ -74,17 +75,3 @@ export async function waitForDiscordGatewayStop( params.registerForceStop?.(onForceStop); }); } - -function toLintErrorObject(value: unknown, fallbackMessage: string): Error { - if (value instanceof Error) { - return value; - } - if (typeof value === "string") { - return new Error(value); - } - const error = new Error(fallbackMessage, { cause: value }); - if ((typeof value === "object" && value !== null) || typeof value === "function") { - Object.assign(error, value); - } - return error; -} diff --git a/extensions/line/src/bot-handlers.ts b/extensions/line/src/bot-handlers.ts index 2ded7cbbda85..641fe7e13123 100644 --- a/extensions/line/src/bot-handlers.ts +++ b/extensions/line/src/bot-handlers.ts @@ -14,6 +14,7 @@ import { resolvePairingIdLabel, upsertChannelPairingRequest, } from "openclaw/plugin-sdk/conversation-runtime"; +import { toErrorObject } from "openclaw/plugin-sdk/error-runtime"; import { DEFAULT_GROUP_HISTORY_LIMIT, createChannelHistoryWindow, @@ -529,7 +530,7 @@ export async function handleLineWebhookEvents( } } if (firstError) { - throw toLintErrorObject(firstError, "Non-Error thrown"); + throw toErrorObject(firstError, "Non-Error thrown"); } } @@ -560,17 +561,3 @@ async function handleLineWebhookEvent( logVerbose(`line: unhandled event type: ${(event as WebhookEvent).type}`); } } - -function toLintErrorObject(value: unknown, fallbackMessage: string): Error { - if (value instanceof Error) { - return value; - } - if (typeof value === "string") { - return new Error(value); - } - const error = new Error(fallbackMessage, { cause: value }); - if ((typeof value === "object" && value !== null) || typeof value === "function") { - Object.assign(error, value); - } - return error; -} diff --git a/extensions/memory-core/src/memory/manager.ts b/extensions/memory-core/src/memory/manager.ts index ac72139bfda2..3e32e941af3a 100644 --- a/extensions/memory-core/src/memory/manager.ts +++ b/extensions/memory-core/src/memory/manager.ts @@ -2,7 +2,11 @@ import type { DatabaseSync } from "node:sqlite"; import type { FSWatcher } from "chokidar"; import { resolveAgentConfig } from "openclaw/plugin-sdk/agent-runtime"; -import { formatErrorMessage, readErrorName } from "openclaw/plugin-sdk/error-runtime"; +import { + formatErrorMessage, + readErrorName, + toErrorObject, +} from "openclaw/plugin-sdk/error-runtime"; import { listRegisteredMemoryEmbeddingProviderAdapters } from "openclaw/plugin-sdk/memory-core-host-embedding-registry"; import { classifyMemoryMultimodalPath } from "openclaw/plugin-sdk/memory-core-host-engine-embeddings"; import { @@ -972,7 +976,7 @@ export class MemoryIndexManager extends MemoryManagerEmbeddingOps implements Mem } } if (closeFailed) { - throw toLintErrorObject(firstError, "Embedding provider retirement failed"); + throw toErrorObject(firstError, "Embedding provider retirement failed"); } }); this.providerRetirementPromise = retirement; @@ -2331,7 +2335,7 @@ export class MemoryIndexManager extends MemoryManagerEmbeddingOps implements Mem private async retryFailedClose(): Promise { const retirementErrors = await this.drainPendingProviderRetirements(); if (this.providersPendingRetirement.size > 0) { - throw toLintErrorObject(retirementErrors.at(-1), "Embedding provider retirement failed"); + throw toErrorObject(retirementErrors.at(-1), "Embedding provider retirement failed"); } if (INDEX_CACHE.get(this.cacheKey) === this) { INDEX_CACHE.delete(this.cacheKey); @@ -2446,7 +2450,7 @@ export class MemoryIndexManager extends MemoryManagerEmbeddingOps implements Mem (this.providersPendingRetirement.size > 0 ? retirementErrors.at(-1) : undefined) ?? closeErrors.values().next().value; if (closeError) { - throw toLintErrorObject(closeError, "Non-Error thrown"); + throw toErrorObject(closeError, "Non-Error thrown"); } if (INDEX_CACHE.get(this.cacheKey) === this) { INDEX_CACHE.delete(this.cacheKey); @@ -2461,17 +2465,4 @@ function hasTargetedSessionSyncParams(params: MemorySyncParams | undefined): boo ); } -function toLintErrorObject(value: unknown, fallbackMessage: string): Error { - if (value instanceof Error) { - return value; - } - if (typeof value === "string") { - return new Error(value); - } - const error = new Error(fallbackMessage, { cause: value }); - if ((typeof value === "object" && value !== null) || typeof value === "function") { - Object.assign(error, value); - } - return error; -} /* oxlint-disable max-lines -- TODO: split this grandfathered oversized file. */ diff --git a/extensions/qa-channel/src/bus-client.ts b/extensions/qa-channel/src/bus-client.ts index b9b25d574fcd..658d810f51b0 100644 --- a/extensions/qa-channel/src/bus-client.ts +++ b/extensions/qa-channel/src/bus-client.ts @@ -1,6 +1,7 @@ // Qa Channel plugin module implements bus client behavior. import http from "node:http"; import https from "node:https"; +import { toErrorObject } from "openclaw/plugin-sdk/error-runtime"; import { resolvePositiveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime"; import { readProviderJsonResponse } from "openclaw/plugin-sdk/provider-http"; import { @@ -124,7 +125,7 @@ async function postJson( resolve(parsed as T); }, (error: unknown) => { - reject(toLintErrorObject(error, "Non-Error rejection")); + reject(toErrorObject(error, "Non-Error rejection")); }, ); response.on("error", reject); @@ -286,17 +287,3 @@ export async function getQaBusState(baseUrl: string): Promise