From 03c64df39fe7ab65585e156eda8c4010fdbbd846 Mon Sep 17 00:00:00 2001 From: Brad Groux <3053586+BradGroux@users.noreply.github.com> Date: Wed, 1 Apr 2026 21:06:44 -0500 Subject: [PATCH] fix(msteams): use formatUnknownError instead of String(err) for error logging (#59321) Replaces String(err) with the existing formatUnknownError() utility across the msteams extension to prevent [object Object] appearing in error logs when non-Error objects are caught (e.g., Axios errors, Bot Framework SDK error objects). Fixes #53910 thanks @bradgroux --- extensions/msteams/src/channel.ts | 3 ++- extensions/msteams/src/feedback-reflection.ts | 9 +++++---- extensions/msteams/src/monitor-handler.ts | 13 +++++++------ .../msteams/src/monitor-handler/message-handler.ts | 10 +++++----- extensions/msteams/src/monitor.ts | 10 +++++----- extensions/msteams/src/reply-dispatcher.ts | 2 +- extensions/msteams/src/reply-stream-controller.ts | 3 ++- extensions/msteams/src/sdk.ts | 3 ++- extensions/msteams/src/send-context.ts | 3 ++- extensions/msteams/src/setup-surface.ts | 3 ++- extensions/msteams/src/streaming-message.ts | 3 ++- 11 files changed, 35 insertions(+), 27 deletions(-) diff --git a/extensions/msteams/src/channel.ts b/extensions/msteams/src/channel.ts index 9749e695d73..3b79012feca 100644 --- a/extensions/msteams/src/channel.ts +++ b/extensions/msteams/src/channel.ts @@ -30,6 +30,7 @@ import { } from "../runtime-api.js"; import { msTeamsApprovalAuth } from "./approval-auth.js"; import { MSTeamsChannelConfigSchema } from "./config-schema.js"; +import { formatUnknownError } from "./errors.js"; import { resolveMSTeamsGroupToolPolicy } from "./policy.js"; import type { ProbeMSTeamsResult } from "./probe.js"; import { @@ -488,7 +489,7 @@ export const msteamsPlugin: ChannelPlugin { - params.log.debug?.("reflection reply error", { error: String(err) }); + params.log.debug?.("reflection reply error", { error: formatUnknownError(err) }); }, }); @@ -207,7 +208,7 @@ export async function runFeedbackReflection(params: RunFeedbackReflectionParams) replyOptions: capture.replyOptions, }); } catch (err) { - log.error("reflection dispatch failed", { error: String(err) }); + log.error("reflection dispatch failed", { error: formatUnknownError(err) }); return; } @@ -237,7 +238,7 @@ export async function runFeedbackReflection(params: RunFeedbackReflectionParams) learning: parsedReflection.learning, }); } catch (err) { - log.debug?.("failed to store reflection learning", { error: String(err) }); + log.debug?.("failed to store reflection learning", { error: formatUnknownError(err) }); } const conversationType = params.conversationRef.conversation?.conversationType?.toLowerCase(); @@ -265,7 +266,7 @@ export async function runFeedbackReflection(params: RunFeedbackReflectionParams) }); log.info("sent reflection follow-up", { sessionKey }); } catch (err) { - log.debug?.("failed to send reflection follow-up", { error: String(err) }); + log.debug?.("failed to send reflection follow-up", { error: formatUnknownError(err) }); } } diff --git a/extensions/msteams/src/monitor-handler.ts b/extensions/msteams/src/monitor-handler.ts index 6ae4d430e7f..d77370eeeaf 100644 --- a/extensions/msteams/src/monitor-handler.ts +++ b/extensions/msteams/src/monitor-handler.ts @@ -1,5 +1,6 @@ import { type OpenClawConfig, type RuntimeEnv } from "../runtime-api.js"; import type { MSTeamsConversationStore } from "./conversation-store.js"; +import { formatUnknownError } from "./errors.js"; import { buildFeedbackEvent, runFeedbackReflection } from "./feedback-reflection.js"; import { buildFileInfoCard, parseFileConsentInvoke, uploadToConsentUrl } from "./file-consent.js"; import { normalizeMSTeamsConversationId } from "./inbound.js"; @@ -168,7 +169,7 @@ async function handleFileConsentInvoke( uniqueId: consentResponse.uploadInfo.uniqueId, }); } catch (err) { - log.error("file upload failed", { uploadId, error: String(err) }); + log.error("file upload failed", { uploadId, error: formatUnknownError(err) }); await context.sendActivity("File upload failed. Please try again."); } finally { removePendingUpload(uploadId); @@ -338,7 +339,7 @@ async function handleFeedbackInvoke( userComment, log: deps.log, }).catch((err) => { - deps.log.error("feedback reflection failed", { error: String(err) }); + deps.log.error("feedback reflection failed", { error: formatUnknownError(err) }); }); } @@ -372,7 +373,7 @@ export function registerMSTeamsHandlers( }, }); } catch (err) { - deps.log.debug?.("file consent handler error", { error: String(err) }); + deps.log.debug?.("file consent handler error", { error: formatUnknownError(err) }); } return; } @@ -396,7 +397,7 @@ export function registerMSTeamsHandlers( try { await handleTeamsMessage(context as MSTeamsTurnContext); } catch (err) { - deps.runtime.error?.(`msteams handler failed: ${String(err)}`); + deps.runtime.error?.(`msteams handler failed: ${formatUnknownError(err)}`); } await next(); }); @@ -432,7 +433,7 @@ export function registerMSTeamsHandlers( }); deps.log.info("sent welcome card"); } catch (err) { - deps.log.debug?.("failed to send welcome card", { error: String(err) }); + deps.log.debug?.("failed to send welcome card", { error: formatUnknownError(err) }); } } else if (!isPersonal && msteamsCfg?.groupWelcomeCard === true) { const botName = ctx.activity?.recipient?.name ?? undefined; @@ -440,7 +441,7 @@ export function registerMSTeamsHandlers( await ctx.sendActivity(buildGroupWelcomeText(botName)); deps.log.info("sent group welcome message"); } catch (err) { - deps.log.debug?.("failed to send group welcome", { error: String(err) }); + deps.log.debug?.("failed to send group welcome", { error: formatUnknownError(err) }); } } else { deps.log.debug?.("skipping welcome (disabled by config or conversation type)"); diff --git a/extensions/msteams/src/monitor-handler/message-handler.ts b/extensions/msteams/src/monitor-handler/message-handler.ts index 24b160b3990..9c15f485128 100644 --- a/extensions/msteams/src/monitor-handler/message-handler.ts +++ b/extensions/msteams/src/monitor-handler/message-handler.ts @@ -476,7 +476,7 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) { threadContext = formatted; } } catch (err) { - log.debug?.("failed to fetch thread history", { error: String(err) }); + log.debug?.("failed to fetch thread history", { error: formatUnknownError(err) }); // Graceful degradation: thread history is an optional enhancement. } } @@ -566,7 +566,7 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) { sessionKey: ctxPayload.SessionKey ?? route.sessionKey, ctx: ctxPayload, onRecordError: (err) => { - logVerboseMessage(`msteams: failed updating session meta: ${String(err)}`); + logVerboseMessage(`msteams: failed updating session meta: ${formatUnknownError(err)}`); }, }); @@ -643,8 +643,8 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) { }); } } catch (err) { - log.error("dispatch failed", { error: String(err) }); - runtime.error?.(`msteams dispatch failed: ${String(err)}`); + log.error("dispatch failed", { error: formatUnknownError(err) }); + runtime.error?.(`msteams dispatch failed: ${formatUnknownError(err)}`); try { await context.sendActivity("⚠️ Something went wrong. Please try again."); } catch { @@ -707,7 +707,7 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) { }); }, onError: (err) => { - runtime.error?.(`msteams debounce flush failed: ${String(err)}`); + runtime.error?.(`msteams debounce flush failed: ${formatUnknownError(err)}`); }, }); diff --git a/extensions/msteams/src/monitor.ts b/extensions/msteams/src/monitor.ts index 1772604d670..319d5a13306 100644 --- a/extensions/msteams/src/monitor.ts +++ b/extensions/msteams/src/monitor.ts @@ -196,7 +196,7 @@ export async function monitorMSTeamsProvider( } } } catch (err) { - runtime.log?.(`msteams resolve failed; using config entries. ${String(err)}`); + runtime.log?.(`msteams resolve failed; using config entries. ${formatUnknownError(err)}`); } msteamsCfg = { @@ -285,7 +285,7 @@ export async function monitorMSTeamsProvider( next(); }) .catch((err) => { - log.debug?.(`JWT validation error: ${String(err)}`); + log.debug?.(`JWT validation error: ${formatUnknownError(err)}`); res.status(401).json({ error: "Unauthorized" }); }); }); @@ -330,7 +330,7 @@ export async function monitorMSTeamsProvider( }; const onError = (err: unknown) => { httpServer.off("listening", onListening); - log.error("msteams server error", { error: String(err) }); + log.error("msteams server error", { error: formatUnknownError(err) }); reject(err); }; httpServer.once("listening", onListening); @@ -339,7 +339,7 @@ export async function monitorMSTeamsProvider( applyMSTeamsWebhookTimeouts(httpServer); httpServer.on("error", (err) => { - log.error("msteams server error", { error: String(err) }); + log.error("msteams server error", { error: formatUnknownError(err) }); }); const shutdown = async () => { @@ -347,7 +347,7 @@ export async function monitorMSTeamsProvider( return new Promise((resolve) => { httpServer.close((err) => { if (err) { - log.debug?.("msteams server close error", { error: String(err) }); + log.debug?.("msteams server close error", { error: formatUnknownError(err) }); } resolve(); }); diff --git a/extensions/msteams/src/reply-dispatcher.ts b/extensions/msteams/src/reply-dispatcher.ts index 09e00985123..bfcd1740d41 100644 --- a/extensions/msteams/src/reply-dispatcher.ts +++ b/extensions/msteams/src/reply-dispatcher.ts @@ -231,7 +231,7 @@ export function createMSTeamsReplyDispatcher(params: { }) .then(() => { return streamController.finalize().catch((err) => { - params.log.debug?.("stream finalize failed", { error: String(err) }); + params.log.debug?.("stream finalize failed", { error: formatUnknownError(err) }); }); }) .finally(() => { diff --git a/extensions/msteams/src/reply-stream-controller.ts b/extensions/msteams/src/reply-stream-controller.ts index 5436e2fe4e8..e11eedea9d8 100644 --- a/extensions/msteams/src/reply-stream-controller.ts +++ b/extensions/msteams/src/reply-stream-controller.ts @@ -1,4 +1,5 @@ import type { ReplyPayload } from "../runtime-api.js"; +import { formatUnknownError } from "./errors.js"; import type { MSTeamsMonitorLogger } from "./monitor-types.js"; import type { MSTeamsTurnContext } from "./sdk-types.js"; import { TeamsHttpStream } from "./streaming-message.js"; @@ -28,7 +29,7 @@ export function createTeamsReplyStreamController(params: { sendActivity: (activity) => params.context.sendActivity(activity), feedbackLoopEnabled: params.feedbackLoopEnabled, onError: (err) => { - params.log.debug?.(`stream error: ${err instanceof Error ? err.message : String(err)}`); + params.log.debug?.(`stream error: ${formatUnknownError(err)}`); }, }) : undefined; diff --git a/extensions/msteams/src/sdk.ts b/extensions/msteams/src/sdk.ts index a108b4eac43..b7a82f343e1 100644 --- a/extensions/msteams/src/sdk.ts +++ b/extensions/msteams/src/sdk.ts @@ -1,3 +1,4 @@ +import { formatUnknownError } from "./errors.js"; import type { MSTeamsAdapter } from "./messenger.js"; import type { MSTeamsCredentials } from "./token.js"; import { buildUserAgent } from "./user-agent.js"; @@ -449,7 +450,7 @@ export function createMSTeamsAdapter(app: MSTeamsApp, sdk: MSTeamsTeamsSdk): MST } } catch (err) { if (!isInvoke) { - response.status(500).send({ error: String(err) }); + response.status(500).send({ error: formatUnknownError(err) }); } } }, diff --git a/extensions/msteams/src/send-context.ts b/extensions/msteams/src/send-context.ts index 2e991a099c6..6b10585c10b 100644 --- a/extensions/msteams/src/send-context.ts +++ b/extensions/msteams/src/send-context.ts @@ -9,6 +9,7 @@ import type { MSTeamsConversationStore, StoredConversationReference, } from "./conversation-store.js"; +import { formatUnknownError } from "./errors.js"; import { resolveGraphChatId } from "./graph-upload.js"; import type { MSTeamsAdapter } from "./messenger.js"; import { getMSTeamsRuntime } from "./runtime.js"; @@ -190,7 +191,7 @@ export async function resolveMSTeamsSendContext(params: { "failed to resolve Graph chat ID; file uploads may fall back to Bot Framework ID", { conversationId, - error: String(err), + error: formatUnknownError(err), }, ); graphChatId = null; diff --git a/extensions/msteams/src/setup-surface.ts b/extensions/msteams/src/setup-surface.ts index 44c2390dcd8..032e2095b4c 100644 --- a/extensions/msteams/src/setup-surface.ts +++ b/extensions/msteams/src/setup-surface.ts @@ -13,6 +13,7 @@ import { type WizardPrompter, } from "openclaw/plugin-sdk/setup"; import type { MSTeamsTeamConfig } from "../runtime-api.js"; +import { formatUnknownError } from "./errors.js"; import { parseMSTeamsTeamEntry, resolveMSTeamsChannelAllowlist, @@ -240,7 +241,7 @@ async function resolveMSTeamsGroupAllowlist(params: { return resolvedEntries; } catch (err) { await params.prompter.note( - `Channel lookup failed; keeping entries as typed. ${String(err)}`, + `Channel lookup failed; keeping entries as typed. ${formatUnknownError(err)}`, "MS Teams channels", ); return resolvedEntries; diff --git a/extensions/msteams/src/streaming-message.ts b/extensions/msteams/src/streaming-message.ts index 5ddb19851ac..25ee75efb68 100644 --- a/extensions/msteams/src/streaming-message.ts +++ b/extensions/msteams/src/streaming-message.ts @@ -35,6 +35,7 @@ export type TeamsStreamOptions = { }; import { AI_GENERATED_ENTITY } from "./ai-entity.js"; +import { formatUnknownError } from "./errors.js"; function extractId(response: unknown): string | undefined { if (response && typeof response === "object" && "id" in response) { @@ -264,7 +265,7 @@ export class TeamsHttpStream { const axiosData = (err as { response?: { data?: unknown; status?: number } })?.response; const statusCode = axiosData?.status ?? (err as { statusCode?: number })?.statusCode; const responseBody = axiosData?.data ? JSON.stringify(axiosData.data).slice(0, 300) : ""; - const msg = err instanceof Error ? err.message : String(err); + const msg = formatUnknownError(err); this.onError?.( new Error( `stream POST failed (HTTP ${statusCode ?? "?"}): ${msg}${responseBody ? ` body=${responseBody}` : ""}`,