From 6b525023d4d2234408cabeaf4db5f46dd647d80b Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 18 Apr 2026 18:35:08 +0100 Subject: [PATCH] fix: polish Slack thread starter context (#68594) --- CHANGELOG.md | 1 + extensions/slack/src/monitor/context.ts | 5 ++- extensions/slack/src/monitor/dm-auth.ts | 3 +- .../slack/src/monitor/events/channels.ts | 13 +++++-- .../slack/src/monitor/events/members.ts | 5 ++- .../slack/src/monitor/events/messages.ts | 5 +-- extensions/slack/src/monitor/events/pins.ts | 3 +- .../slack/src/monitor/events/reactions.ts | 3 +- extensions/slack/src/monitor/media.test.ts | 35 +++++++++++++++++-- extensions/slack/src/monitor/media.ts | 29 ++++++--------- .../slack/src/monitor/message-handler.ts | 3 +- .../src/monitor/message-handler/dispatch.ts | 17 ++++----- .../src/monitor/message-handler/prepare.ts | 7 ++-- extensions/slack/src/monitor/provider.ts | 10 ++++-- extensions/slack/src/monitor/slash.ts | 13 ++++--- .../slack/src/monitor/thread-resolution.ts | 3 +- scripts/check-no-raw-channel-fetch.mjs | 6 ++-- 17 files changed, 109 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8679be724b8..2c7f26e341a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,6 +69,7 @@ Docs: https://docs.openclaw.ai - Gateway/restart: keep stale-gateway cleanup from terminating the current process's parent or ancestors, so plugin sidecars like WeChat no longer kill the active gateway and trigger an infinite supervisor restart loop. Fixes #68451. (#68517) Thanks @openperf. - Gateway/auth: reject gateway auth credentials that match published example placeholders at startup and secret reload, and keep cloud install snippets from publishing copy-paste gateway/keyring secrets. (#68404) Thanks @coygeek. - CLI/update: preserve macOS restart helper launchctl failures in the update restart log without letting log setup block the restart path. (#68492) Thanks @hclsys. +- Slack/threads: keep file-only root messages as starter context so first thread replies can still hydrate starter media. (#68594) Thanks @martingarramon. ## 2026.4.15 diff --git a/extensions/slack/src/monitor/context.ts b/extensions/slack/src/monitor/context.ts index db9268ba54e..d04d4b5953d 100644 --- a/extensions/slack/src/monitor/context.ts +++ b/extensions/slack/src/monitor/context.ts @@ -6,6 +6,7 @@ import type { } from "openclaw/plugin-sdk/config-runtime"; import type { SessionScope } from "openclaw/plugin-sdk/config-runtime"; import type { DmPolicy, GroupPolicy } from "openclaw/plugin-sdk/config-runtime"; +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import { createDedupeCache } from "openclaw/plugin-sdk/infra-runtime"; import type { HistoryEntry } from "openclaw/plugin-sdk/reply-history"; import { resolveAgentRoute } from "openclaw/plugin-sdk/routing"; @@ -282,7 +283,9 @@ export function createSlackMonitorContext(params: { status: p.status, }); } catch (err) { - logVerbose(`slack status update failed for channel ${p.channelId}: ${String(err)}`); + logVerbose( + `slack status update failed for channel ${p.channelId}: ${formatErrorMessage(err)}`, + ); } }; diff --git a/extensions/slack/src/monitor/dm-auth.ts b/extensions/slack/src/monitor/dm-auth.ts index f71e65b3182..fa360d5cfde 100644 --- a/extensions/slack/src/monitor/dm-auth.ts +++ b/extensions/slack/src/monitor/dm-auth.ts @@ -1,5 +1,6 @@ import { formatAllowlistMatchMeta } from "openclaw/plugin-sdk/allow-from"; import { createChannelPairingChallengeIssuer } from "openclaw/plugin-sdk/channel-pairing"; +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import { resolveSlackAllowListMatch } from "./allow-list.js"; import type { SlackMonitorContext } from "./context.js"; import { upsertChannelPairingRequest } from "./conversation.runtime.js"; @@ -57,7 +58,7 @@ export async function authorizeSlackDirectMessage(params: { ); }, onReplyError: (err) => { - params.log(`slack pairing reply failed for ${params.senderId}: ${String(err)}`); + params.log(`slack pairing reply failed for ${params.senderId}: ${formatErrorMessage(err)}`); }, }); return false; diff --git a/extensions/slack/src/monitor/events/channels.ts b/extensions/slack/src/monitor/events/channels.ts index e4ceb166ada..159adcfc5e1 100644 --- a/extensions/slack/src/monitor/events/channels.ts +++ b/extensions/slack/src/monitor/events/channels.ts @@ -1,6 +1,7 @@ import type { SlackEventMiddlewareArgs } from "@slack/bolt"; import { resolveChannelConfigWrites } from "openclaw/plugin-sdk/channel-config-writes"; import { loadConfig, writeConfigFile } from "openclaw/plugin-sdk/config-runtime"; +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import { enqueueSystemEvent } from "openclaw/plugin-sdk/infra-runtime"; import { danger, warn } from "openclaw/plugin-sdk/runtime-env"; import { migrateSlackChannelConfig } from "../../channel-migration.js"; @@ -61,7 +62,9 @@ export function registerSlackChannelEvents(params: { const channelName = payload.channel?.name; enqueueChannelSystemEvent({ kind: "created", channelId, channelName }); } catch (err) { - ctx.runtime.error?.(danger(`slack channel created handler failed: ${String(err)}`)); + ctx.runtime.error?.( + danger(`slack channel created handler failed: ${formatErrorMessage(err)}`), + ); } }, ); @@ -80,7 +83,9 @@ export function registerSlackChannelEvents(params: { const channelName = payload.channel?.name_normalized ?? payload.channel?.name; enqueueChannelSystemEvent({ kind: "renamed", channelId, channelName }); } catch (err) { - ctx.runtime.error?.(danger(`slack channel rename handler failed: ${String(err)}`)); + ctx.runtime.error?.( + danger(`slack channel rename handler failed: ${formatErrorMessage(err)}`), + ); } }, ); @@ -155,7 +160,9 @@ export function registerSlackChannelEvents(params: { ); } } catch (err) { - ctx.runtime.error?.(danger(`slack channel_id_changed handler failed: ${String(err)}`)); + ctx.runtime.error?.( + danger(`slack channel_id_changed handler failed: ${formatErrorMessage(err)}`), + ); } }, ); diff --git a/extensions/slack/src/monitor/events/members.ts b/extensions/slack/src/monitor/events/members.ts index 26d02f11613..1d5e12ce8bd 100644 --- a/extensions/slack/src/monitor/events/members.ts +++ b/extensions/slack/src/monitor/events/members.ts @@ -1,4 +1,5 @@ import type { SlackEventMiddlewareArgs } from "@slack/bolt"; +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import { enqueueSystemEvent } from "openclaw/plugin-sdk/infra-runtime"; import { danger } from "openclaw/plugin-sdk/runtime-env"; import type { SlackMonitorContext } from "../context.js"; @@ -42,7 +43,9 @@ export function registerSlackMemberEvents(params: { contextKey: `slack:member:${params.verb}:${channelId ?? "unknown"}:${payload.user ?? "unknown"}`, }); } catch (err) { - ctx.runtime.error?.(danger(`slack ${params.verb} handler failed: ${String(err)}`)); + ctx.runtime.error?.( + danger(`slack ${params.verb} handler failed: ${formatErrorMessage(err)}`), + ); } }; diff --git a/extensions/slack/src/monitor/events/messages.ts b/extensions/slack/src/monitor/events/messages.ts index 309308caa57..298bd858c40 100644 --- a/extensions/slack/src/monitor/events/messages.ts +++ b/extensions/slack/src/monitor/events/messages.ts @@ -1,4 +1,5 @@ import type { SlackEventMiddlewareArgs } from "@slack/bolt"; +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import { enqueueSystemEvent } from "openclaw/plugin-sdk/infra-runtime"; import { danger } from "openclaw/plugin-sdk/runtime-env"; import type { SlackAppMentionEvent, SlackMessageEvent } from "../../types.js"; @@ -43,7 +44,7 @@ export function registerSlackMessageEvents(params: { await handleSlackMessage(message, { source: "message" }); } catch (err) { - ctx.runtime.error?.(danger(`slack handler failed: ${String(err)}`)); + ctx.runtime.error?.(danger(`slack handler failed: ${formatErrorMessage(err)}`)); } }; @@ -77,7 +78,7 @@ export function registerSlackMessageEvents(params: { wasMentioned: true, }); } catch (err) { - ctx.runtime.error?.(danger(`slack mention handler failed: ${String(err)}`)); + ctx.runtime.error?.(danger(`slack mention handler failed: ${formatErrorMessage(err)}`)); } }); } diff --git a/extensions/slack/src/monitor/events/pins.ts b/extensions/slack/src/monitor/events/pins.ts index ba95f515810..6708487fe78 100644 --- a/extensions/slack/src/monitor/events/pins.ts +++ b/extensions/slack/src/monitor/events/pins.ts @@ -1,4 +1,5 @@ import type { SlackEventMiddlewareArgs } from "@slack/bolt"; +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import { enqueueSystemEvent } from "openclaw/plugin-sdk/infra-runtime"; import { danger } from "openclaw/plugin-sdk/runtime-env"; import type { SlackMonitorContext } from "../context.js"; @@ -45,7 +46,7 @@ async function handleSlackPinEvent(params: { }, ); } catch (err) { - ctx.runtime.error?.(danger(`slack ${errorLabel} handler failed: ${String(err)}`)); + ctx.runtime.error?.(danger(`slack ${errorLabel} handler failed: ${formatErrorMessage(err)}`)); } } diff --git a/extensions/slack/src/monitor/events/reactions.ts b/extensions/slack/src/monitor/events/reactions.ts index f439168dfde..3cb57fedcf0 100644 --- a/extensions/slack/src/monitor/events/reactions.ts +++ b/extensions/slack/src/monitor/events/reactions.ts @@ -1,4 +1,5 @@ import type { SlackEventMiddlewareArgs } from "@slack/bolt"; +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import { enqueueSystemEvent } from "openclaw/plugin-sdk/infra-runtime"; import { danger } from "openclaw/plugin-sdk/runtime-env"; import type { SlackMonitorContext } from "../context.js"; @@ -46,7 +47,7 @@ export function registerSlackReactionEvents(params: { contextKey: `slack:reaction:${action}:${item.channel}:${item.ts}:${event.user}:${emojiLabel}`, }); } catch (err) { - ctx.runtime.error?.(danger(`slack reaction handler failed: ${String(err)}`)); + ctx.runtime.error?.(danger(`slack reaction handler failed: ${formatErrorMessage(err)}`)); } }; diff --git a/extensions/slack/src/monitor/media.test.ts b/extensions/slack/src/monitor/media.test.ts index f543ef2375b..871afeaeab0 100644 --- a/extensions/slack/src/monitor/media.test.ts +++ b/extensions/slack/src/monitor/media.test.ts @@ -905,7 +905,7 @@ describe("resolveSlackThreadStarter", () => { expect(vi.mocked(logVerbose)).not.toHaveBeenCalled(); }); - it("returns null when the starter message has no text and no files", async () => { + it("returns null when the starter message has no text or files", async () => { const replies = vi.fn().mockResolvedValueOnce({ messages: [{ text: " ", user: "U1" }] }); const client = { conversations: { replies }, @@ -921,6 +921,37 @@ describe("resolveSlackThreadStarter", () => { expect(vi.mocked(logVerbose)).not.toHaveBeenCalled(); }); + it("returns a placeholder starter when the root message only has files", async () => { + const replies = vi.fn().mockResolvedValueOnce({ + messages: [ + { + text: " ", + user: "U1", + ts: "1.000", + files: [{ name: "root.png", mimetype: "image/png" }], + }, + ], + }); + const client = { + conversations: { replies }, + } as unknown as Parameters[0]["client"]; + + const result = await resolveSlackThreadStarter({ + channelId: "C1", + threadTs: "1.000", + client, + }); + + expect(result).toEqual({ + text: "[attached: root.png]", + userId: "U1", + botId: undefined, + ts: "1.000", + files: [{ name: "root.png", mimetype: "image/png" }], + }); + expect(vi.mocked(logVerbose)).not.toHaveBeenCalled(); + }); + it("returns null and surfaces the error via logVerbose when Slack API throws", async () => { const replies = vi.fn().mockRejectedValueOnce(new Error("not_in_channel")); const client = { @@ -942,7 +973,7 @@ describe("resolveSlackThreadStarter", () => { expect(vi.mocked(logVerbose)).toHaveBeenCalledWith(expect.stringContaining("ts=9.999")); }); - it("surfaces non-Error thrown values as String(err) via logVerbose", async () => { + it("surfaces non-Error thrown values via logVerbose", async () => { const replies = vi.fn().mockRejectedValueOnce("rate_limited"); const client = { conversations: { replies }, diff --git a/extensions/slack/src/monitor/media.ts b/extensions/slack/src/monitor/media.ts index 2743f8e48c2..2c67cc065e1 100644 --- a/extensions/slack/src/monitor/media.ts +++ b/extensions/slack/src/monitor/media.ts @@ -1,4 +1,5 @@ import type { WebClient as SlackWebClient } from "@slack/web-api"; +import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime"; import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import { normalizeHostname } from "openclaw/plugin-sdk/host-runtime"; import { fetchWithRuntimeDispatcher } from "openclaw/plugin-sdk/infra-runtime"; @@ -385,18 +386,11 @@ function evictThreadStarterCache(): void { THREAD_STARTER_CACHE.delete(cacheKey); } } - if (THREAD_STARTER_CACHE.size <= THREAD_STARTER_CACHE_MAX) { - return; - } - const excess = THREAD_STARTER_CACHE.size - THREAD_STARTER_CACHE_MAX; - let removed = 0; - for (const cacheKey of THREAD_STARTER_CACHE.keys()) { - THREAD_STARTER_CACHE.delete(cacheKey); - removed += 1; - if (removed >= excess) { - break; - } - } + pruneMapToMaxSize(THREAD_STARTER_CACHE, THREAD_STARTER_CACHE_MAX); +} + +function formatSlackFilePlaceholder(files: SlackFile[] | undefined): string { + return `[attached: ${files?.map((file) => file.name ?? "file").join(", ") ?? "file"}]`; } export async function resolveSlackThreadStarter(params: { @@ -430,15 +424,16 @@ export async function resolveSlackThreadStarter(params: { }; const message = response?.messages?.[0]; const text = (message?.text ?? "").trim(); - if (!message || !text) { + const files = message?.files?.length ? message.files : undefined; + if (!message || (!text && !files)) { return null; } const starter: SlackThreadStarter = { - text, + text: text || formatSlackFilePlaceholder(files), userId: message.user, botId: message.bot_id, ts: message.ts, - files: message.files, + files, }; if (THREAD_STARTER_CACHE.has(cacheKey)) { THREAD_STARTER_CACHE.delete(cacheKey); @@ -536,9 +531,7 @@ export async function resolveSlackThreadHistory(params: { return retained.map((msg) => ({ // For file-only messages, create a placeholder showing attached filenames - text: msg.text?.trim() - ? msg.text - : `[attached: ${msg.files?.map((f) => f.name ?? "file").join(", ")}]`, + text: msg.text?.trim() ? msg.text : formatSlackFilePlaceholder(msg.files), userId: msg.user, botId: msg.bot_id, ts: msg.ts, diff --git a/extensions/slack/src/monitor/message-handler.ts b/extensions/slack/src/monitor/message-handler.ts index d5ecf1f7ac9..d4c404e62bf 100644 --- a/extensions/slack/src/monitor/message-handler.ts +++ b/extensions/slack/src/monitor/message-handler.ts @@ -2,6 +2,7 @@ import { createChannelInboundDebouncer, shouldDebounceTextInbound, } from "openclaw/plugin-sdk/channel-inbound"; +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import type { ResolvedSlackAccount } from "../accounts.js"; import type { SlackMessageEvent } from "../types.js"; import { stripSlackMentionsForCommandDetection } from "./commands.js"; @@ -189,7 +190,7 @@ export function createSlackMessageHandler(params: { } }, onError: (err) => { - ctx.runtime.error?.(`slack inbound debounce flush failed: ${String(err)}`); + ctx.runtime.error?.(`slack inbound debounce flush failed: ${formatErrorMessage(err)}`); }, }); const threadTsResolver = createSlackThreadTsResolver({ client: ctx.app.client }); diff --git a/extensions/slack/src/monitor/message-handler/dispatch.ts b/extensions/slack/src/monitor/message-handler/dispatch.ts index 74783f81db7..cdd61b1b4ba 100644 --- a/extensions/slack/src/monitor/message-handler/dispatch.ts +++ b/extensions/slack/src/monitor/message-handler/dispatch.ts @@ -12,6 +12,7 @@ import { resolveChannelStreamingBlockEnabled, resolveChannelStreamingNativeTransport, } from "openclaw/plugin-sdk/channel-streaming"; +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import { resolveAgentOutboundIdentity } from "openclaw/plugin-sdk/outbound-runtime"; import { clearHistoryEntriesIfEnabled } from "openclaw/plugin-sdk/reply-history"; import { resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload"; @@ -199,7 +200,7 @@ export async function resolveSlackStreamRecipientTeamId(params: { return teamId; } } catch (err) { - logVerbose(`slack-stream: users.info team lookup failed (${String(err)})`); + logVerbose(`slack-stream: users.info team lookup failed (${formatErrorMessage(err)})`); } } return params.fallbackTeamId; @@ -273,7 +274,7 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag token: ctx.botToken, client: ctx.app.client, }).catch((err) => { - if (String(err).includes("already_reacted")) { + if (formatErrorMessage(err).includes("already_reacted")) { return; } throw err; @@ -284,7 +285,7 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag token: ctx.botToken, client: ctx.app.client, }).catch((err) => { - if (String(err).includes("no_reaction")) { + if (formatErrorMessage(err).includes("no_reaction")) { return; } throw err; @@ -556,7 +557,7 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag }); } catch (err) { runtime.error?.( - danger(`slack-stream: streaming API call failed: ${String(err)}, falling back`), + danger(`slack-stream: streaming API call failed: ${formatErrorMessage(err)}, falling back`), ); streamFailed = true; await deliverNormally({ @@ -613,7 +614,7 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag return; } catch (err) { logVerbose( - `slack: preview final edit failed; falling back to standard send (${String(err)})`, + `slack: preview final edit failed; falling back to standard send (${formatErrorMessage(err)})`, ); } } else if (previewStreamingEnabled && streamMode === "status_final" && hasStreamedMessage) { @@ -629,7 +630,7 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag }); } } catch (err) { - logVerbose(`slack: status_final completion update failed (${String(err)})`); + logVerbose(`slack: status_final completion update failed (${formatErrorMessage(err)})`); } } else if (reply.hasMedia) { await draftStream?.clear(); @@ -639,7 +640,7 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag await deliverNormally({ payload, kind: info.kind }); }, onError: (err, info) => { - runtime.error?.(danger(`slack ${info.kind} reply failed: ${String(err)}`)); + runtime.error?.(danger(`slack ${info.kind} reply failed: ${formatErrorMessage(err)}`)); replyPipeline.typingCallbacks?.onIdle?.(); }, }); @@ -771,7 +772,7 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag try { await stopSlackStream({ session: finalStream }); } catch (err) { - runtime.error?.(danger(`slack-stream: failed to stop stream: ${String(err)}`)); + runtime.error?.(danger(`slack-stream: failed to stop stream: ${formatErrorMessage(err)}`)); } } diff --git a/extensions/slack/src/monitor/message-handler/prepare.ts b/extensions/slack/src/monitor/message-handler/prepare.ts index 2a753771514..43dfaf44464 100644 --- a/extensions/slack/src/monitor/message-handler/prepare.ts +++ b/extensions/slack/src/monitor/message-handler/prepare.ts @@ -15,6 +15,7 @@ import { import { resolveControlCommandGate } from "openclaw/plugin-sdk/command-auth"; import { hasControlCommand } from "openclaw/plugin-sdk/command-auth"; import { shouldHandleTextCommands } from "openclaw/plugin-sdk/command-auth"; +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import { enqueueSystemEvent } from "openclaw/plugin-sdk/infra-runtime"; import { buildPendingHistoryContextFromMap, @@ -596,7 +597,9 @@ export async function prepareSlackMessage(params: { }).then( () => true, (err) => { - logVerbose(`slack react failed for channel ${message.channel}: ${String(err)}`); + logVerbose( + `slack react failed for channel ${message.channel}: ${formatErrorMessage(err)}`, + ); return false; }, ) @@ -804,7 +807,7 @@ export async function prepareSlackMessage(params: { onRecordError: (err) => { ctx.logger.warn( { - error: String(err), + error: formatErrorMessage(err), storePath, sessionKey, }, diff --git a/extensions/slack/src/monitor/provider.ts b/extensions/slack/src/monitor/provider.ts index 64afeb0c9fc..c2e3aeb4b70 100644 --- a/extensions/slack/src/monitor/provider.ts +++ b/extensions/slack/src/monitor/provider.ts @@ -512,7 +512,9 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) { summarizeMapping("slack channels", mapping, unresolved, runtime); } } catch (err) { - runtime.log?.(`slack channel resolve failed; using config entries. ${String(err)}`); + runtime.log?.( + `slack channel resolve failed; using config entries. ${formatUnknownError(err)}`, + ); } } @@ -533,7 +535,9 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) { ctx.allowFrom = normalizeAllowList(allowFrom); summarizeMapping("slack users", mapping, unresolved, runtime); } catch (err) { - runtime.log?.(`slack user resolve failed; using config entries. ${String(err)}`); + runtime.log?.( + `slack user resolve failed; using config entries. ${formatUnknownError(err)}`, + ); } } @@ -565,7 +569,7 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) { summarizeMapping("slack channel users", mapping, unresolved, runtime); } catch (err) { runtime.log?.( - `slack channel user resolve failed; using config entries. ${String(err)}`, + `slack channel user resolve failed; using config entries. ${formatUnknownError(err)}`, ); } } diff --git a/extensions/slack/src/monitor/slash.ts b/extensions/slack/src/monitor/slash.ts index 5feb6bc35e7..b6f5e2966b6 100644 --- a/extensions/slack/src/monitor/slash.ts +++ b/extensions/slack/src/monitor/slash.ts @@ -10,6 +10,7 @@ import { resolveNativeCommandsEnabled, resolveNativeSkillsEnabled, } from "openclaw/plugin-sdk/config-runtime"; +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import type { ReplyPayload } from "openclaw/plugin-sdk/reply-runtime"; import { danger, logVerbose } from "openclaw/plugin-sdk/runtime-env"; import { chunkItems, normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime"; @@ -600,7 +601,9 @@ export async function registerSlackMonitorSlashCommands(params: { sessionKey: ctxPayload.SessionKey ?? route.sessionKey, ctx: ctxPayload, onError: (err) => - runtime.error?.(danger(`slack slash: failed updating session meta: ${String(err)}`)), + runtime.error?.( + danger(`slack slash: failed updating session meta: ${formatErrorMessage(err)}`), + ), }); const { onModelSelected, ...replyPipeline } = createChannelReplyPipeline({ @@ -632,7 +635,9 @@ export async function registerSlackMonitorSlashCommands(params: { ...replyPipeline, deliver: async (payload) => deliverSlashPayloads([payload]), onError: (err, info) => { - runtime.error?.(danger(`slack slash ${info.kind} reply failed: ${String(err)}`)); + runtime.error?.( + danger(`slack slash ${info.kind} reply failed: ${formatErrorMessage(err)}`), + ); }, }, replyOptions: { @@ -644,7 +649,7 @@ export async function registerSlackMonitorSlashCommands(params: { await deliverSlashPayloads([]); } } catch (err) { - runtime.error?.(danger(`slack slash handler failed: ${String(err)}`)); + runtime.error?.(danger(`slack slash handler failed: ${formatErrorMessage(err)}`)); await respond({ text: "Sorry, something went wrong handling that command.", response_type: "ephemeral", @@ -803,7 +808,7 @@ export async function registerSlackMonitorSlashCommands(params: { } catch (err) { supportsExternalArgMenus = false; logVerbose( - `slack: external arg-menu registration failed, falling back to static menus: ${String(err)}`, + `slack: external arg-menu registration failed, falling back to static menus: ${formatErrorMessage(err)}`, ); } diff --git a/extensions/slack/src/monitor/thread-resolution.ts b/extensions/slack/src/monitor/thread-resolution.ts index 5ecf81a9021..fdec08e981a 100644 --- a/extensions/slack/src/monitor/thread-resolution.ts +++ b/extensions/slack/src/monitor/thread-resolution.ts @@ -1,5 +1,6 @@ import type { WebClient as SlackWebClient } from "@slack/web-api"; import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime"; +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import { logVerbose, shouldLogVerbose } from "openclaw/plugin-sdk/runtime-env"; import type { SlackMessageEvent } from "../types.js"; @@ -35,7 +36,7 @@ async function resolveThreadTsFromHistory(params: { } catch (err) { if (shouldLogVerbose()) { logVerbose( - `slack inbound: failed to resolve thread_ts via conversations.history for channel=${params.channelId} ts=${params.messageTs}: ${String(err)}`, + `slack inbound: failed to resolve thread_ts via conversations.history for channel=${params.channelId} ts=${params.messageTs}: ${formatErrorMessage(err)}`, ); } return undefined; diff --git a/scripts/check-no-raw-channel-fetch.mjs b/scripts/check-no-raw-channel-fetch.mjs index 3ad0fbb699d..c5d35d3dddf 100644 --- a/scripts/check-no-raw-channel-fetch.mjs +++ b/scripts/check-no-raw-channel-fetch.mjs @@ -60,9 +60,9 @@ const allowedRawFetchCallsites = new Set([ bundledPluginCallsite("qqbot", "src/tools/channel.ts", 180), bundledPluginCallsite("qqbot", "src/utils/audio-convert.ts", 377), bundledPluginCallsite("signal", "src/install-signal-cli.ts", 224), - bundledPluginCallsite("slack", "src/monitor/media.ts", 98), - bundledPluginCallsite("slack", "src/monitor/media.ts", 117), - bundledPluginCallsite("slack", "src/monitor/media.ts", 122), + bundledPluginCallsite("slack", "src/monitor/media.ts", 99), + bundledPluginCallsite("slack", "src/monitor/media.ts", 118), + bundledPluginCallsite("slack", "src/monitor/media.ts", 123), bundledPluginCallsite("tlon", "src/tlon-api.ts", 185), bundledPluginCallsite("tlon", "src/tlon-api.ts", 235), bundledPluginCallsite("tlon", "src/tlon-api.ts", 289),