fix(discord): harden read message results (#82276) · openclaw/openclaw@4add9ba
steipete
·
2026-05-16
·
via Recent Commits to openclaw:main
File tree
extensions/discord/src/actions
| Original file line number | Diff line number | Diff line change |
|---|
@@ -18,6 +18,7 @@ Docs: https://docs.openclaw.ai
|
18 | 18 | - Plugin releases: require external package compatibility metadata in the npm plugin publish plan, matching the ClawHub package contract before packages ship. |
19 | 19 | - Agents/OpenAI-compatible: honor per-model `max_completion_tokens`/`max_tokens` params in embedded OpenAI-completions runs so high-token Kimi-style routes keep their configured completion cap. Fixes #82230. Thanks @albert-zen. |
20 | 20 | - Agents/local: install a local gateway request scope around trusted `openclaw agent --local` runs, so subagent completion announces can use in-process gateway dispatch without crashing. Fixes #82140. Thanks @Kushmaro. |
| 21 | +- Discord: validate message-read results before normalizing channel history and report unexpected payloads with a Discord boundary error instead of `map is not a function`. Fixes #82252. Thanks @jessewunderlich. |
21 | 22 | - Telegram/active-memory: run blocking memory recall through the Telegram provider for direct-message turns even when the hook context carries the raw chat id, preventing embedded recall from launching against an invalid numeric channel. Fixes #82177. Thanks @cslash-zz. |
22 | 23 | - Control UI/WebChat: keep optimistic image messages from embedding large inline `data:` previews and preserve image-only user turns in chat history, avoiding browser stack overflows when sending image attachments. Fixes #82182. Thanks @ExploreSheep. |
23 | 24 | - Agents/media: preserve message-tool-only delivery for generated music and video completion handoffs, so group/channel completions do not finish without posting the generated attachment. |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -24,6 +24,29 @@ function parseDiscordMessageLink(link: string) {
|
24 | 24 | }; |
25 | 25 | } |
26 | 26 | |
| 27 | +function describeDiscordMessageListResult(value: unknown): string { |
| 28 | +if (Array.isArray(value)) { |
| 29 | +return "array"; |
| 30 | +} |
| 31 | +if (value === null) { |
| 32 | +return "null"; |
| 33 | +} |
| 34 | +if (value && typeof value === "object") { |
| 35 | +const keys = Object.keys(value).toSorted(); |
| 36 | +return keys.length ? `object with keys ${keys.join(", ")}` : "object"; |
| 37 | +} |
| 38 | +return typeof value; |
| 39 | +} |
| 40 | + |
| 41 | +function assertDiscordMessageListResult(value: unknown): Array<unknown> { |
| 42 | +if (Array.isArray(value)) { |
| 43 | +return value; |
| 44 | +} |
| 45 | +throw new Error( |
| 46 | +`Discord message read returned ${describeDiscordMessageListResult(value)} instead of an array.`, |
| 47 | +); |
| 48 | +} |
| 49 | + |
27 | 50 | export async function handleDiscordMessageManagementAction(ctx: DiscordMessagingActionContext) { |
28 | 51 | switch (ctx.action) { |
29 | 52 | case "permissions": { |
@@ -80,10 +103,8 @@ export async function handleDiscordMessageManagementAction(ctx: DiscordMessaging
|
80 | 103 | after: readStringParam(ctx.params, "after"), |
81 | 104 | around: readStringParam(ctx.params, "around"), |
82 | 105 | }; |
83 | | -const messages = await discordMessagingActionRuntime.readMessagesDiscord( |
84 | | -channelId, |
85 | | -query, |
86 | | -ctx.withOpts(), |
| 106 | +const messages = assertDiscordMessageListResult( |
| 107 | +await discordMessagingActionRuntime.readMessagesDiscord(channelId, query, ctx.withOpts()), |
87 | 108 | ); |
88 | 109 | return jsonResult({ |
89 | 110 | ok: true, |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -389,6 +389,14 @@ describe("handleDiscordMessagingAction", () => {
|
389 | 389 | expect(payload.messages[0].timestampUtc).toBe(new Date(expectedMs).toISOString()); |
390 | 390 | }); |
391 | 391 | |
| 392 | +it("rejects unexpected readMessages payloads with a boundary error", async () => { |
| 393 | +readMessagesDiscord.mockResolvedValueOnce({ ok: true } as never); |
| 394 | + |
| 395 | +await expect( |
| 396 | +handleMessagingAction("readMessages", { channelId: "C1" }, enableAllActions), |
| 397 | +).rejects.toThrow("Discord message read returned object with keys ok instead of an array."); |
| 398 | +}); |
| 399 | + |
392 | 400 | it("threads provided cfg into readMessages calls", async () => { |
393 | 401 | const cfg = { |
394 | 402 | channels: { |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。