Files
openclaw/extensions/discord/src/send.message-request.test.ts
T
Yuval Dinodia df883ab81a fix(discord): stop retrying non-idempotent sends on post-connect-ambiguous errors (#103867)
* fix(discord): stop retrying non-idempotent sends on post-connect-ambiguous errors

Discord's outbound retry runner treated every transient transport error as
retryable, including ECONNRESET, ETIMEDOUT, AbortError, and the undici
headers/body/socket timeouts. Those errors can fire after Discord has already
received and created the message but before the client reads the response, so
replaying the non-idempotent createChannelMessage POST delivers the same
message twice while reporting success.

Add a pre-connect-only classifier (isRetryableDiscordPreConnectError) covering
errors that provably never reached Discord (DNS/connect refused/connect
timeout) plus rate-limit rejections, mirroring the Telegram send-path
carve-out. The retry runner now accepts a per-call nonIdempotent option; the
message-create and thread-create call sites opt in so ambiguous post-connect
errors surface to the caller instead of double-sending. Idempotent REST calls
(reactions, edits, DM-channel lookup, uploads) keep the broader transient set.

* fix(discord): retry nonce-enforced message creates

* fix(discord): scope create retries by endpoint

* fix(discord): stabilize sticker and poll create nonce across retries

* fix(discord): tighten create retry safety

* fix(discord): preserve nonce-safe retries

* fix(discord): scope nonce fields to message creates

* test(discord): cover nonce-safe direct sends

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-11 00:54:23 -07:00

35 lines
1.0 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { buildDiscordMessageRequest } from "./send.message-request.js";
describe("buildDiscordMessageRequest", () => {
it("enforces a supplied nonce across retries", () => {
const body = buildDiscordMessageRequest({
endpoint: "create-message",
text: "hello",
nonce: "stable-create-nonce",
});
expect(body).toMatchObject({
content: "hello",
nonce: "stable-create-nonce",
enforce_nonce: true,
});
});
it("adds a nonce for each logical create", () => {
const body = buildDiscordMessageRequest({ endpoint: "create-message", text: "hello" });
expect(body).toMatchObject({
content: "hello",
enforce_nonce: true,
});
expect(body.nonce).toMatch(/^[0-9a-f]{24}$/);
});
it("omits create-message nonce fields from forum thread starters", () => {
const body = buildDiscordMessageRequest({ endpoint: "forum-thread", text: "hello" });
expect(body).toEqual({ content: "hello" });
});
});