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
This commit is contained in:
Brad Groux
2026-04-01 21:06:44 -05:00
committed by GitHub
parent 474693bdb2
commit 03c64df39f
11 changed files with 35 additions and 27 deletions

View File

@@ -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<ResolvedMSTeamsAccount, ProbeMSTeamsRe
applyResolvedEntry(target, entry);
});
} catch (err) {
runtime.error?.(`msteams resolve failed: ${String(err)}`);
runtime.error?.(`msteams resolve failed: ${formatUnknownError(err)}`);
markPendingLookupFailed(pending);
}
};

View File

@@ -15,6 +15,7 @@ import {
type OpenClawConfig,
} from "../runtime-api.js";
import type { StoredConversationReference } from "./conversation-store.js";
import { formatUnknownError } from "./errors.js";
import { buildReflectionPrompt, parseReflectionResponse } from "./feedback-reflection-prompt.js";
import {
DEFAULT_COOLDOWN_MS,
@@ -137,7 +138,7 @@ function createReflectionCaptureDispatcher(params: {
typingCallbacks: noopTypingCallbacks,
humanDelay: core.channel.reply.resolveHumanDelayConfig(params.cfg, params.agentId),
onError: (err) => {
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) });
}
}

View File

@@ -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<T extends MSTeamsActivityHandler>(
},
});
} 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<T extends MSTeamsActivityHandler>(
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<T extends MSTeamsActivityHandler>(
});
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<T extends MSTeamsActivityHandler>(
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)");

View File

@@ -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)}`);
},
});

View File

@@ -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<void>((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();
});

View File

@@ -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(() => {

View File

@@ -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;

View File

@@ -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) });
}
}
},

View File

@@ -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;

View File

@@ -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;

View File

@@ -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}` : ""}`,