Skip to content

Commit 5699be8

Browse files
committed
fix(discord): remove ETIMEDOUT from pre-dispatch allowlist
ETIMEDOUT can occur both before and after the HTTP request reaches Discord (response timeout after acceptance), so returning not_sent would risk duplicate messages. Keep it unresolved to preserve duplicate-send safety. (#100979)
1 parent c3f6c13 commit 5699be8

2 files changed

Lines changed: 21 additions & 1 deletion

File tree

extensions/discord/src/channel.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ const discordMessageAdapterBase = createChannelMessageAdapterFromOutbound({
114114
* ENETUNREACH). Falls back to `unresolved` otherwise to avoid blindly
115115
* duplicating messages that may have already been delivered. (#100979) */
116116
const PRE_DISPATCH_ERROR_RE =
117-
/\b(UND_ERR_CONNECT_TIMEOUT|ECONNREFUSED|ENOTFOUND|EAI_AGAIN|ENETUNREACH|ERR_CONNECT_TIMEOUT|EADDRNOTAVAIL|ETIMEDOUT)\b/iu;
117+
/\b(UND_ERR_CONNECT_TIMEOUT|ECONNREFUSED|ENOTFOUND|EAI_AGAIN|ENETUNREACH|ERR_CONNECT_TIMEOUT|EADDRNOTAVAIL)\b/iu;
118118

119119
function reconcileDiscordUnknownSend(ctx: {
120120
lastError?: string | null;

extensions/discord/src/durable-delivery.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,4 +169,24 @@ describe("durable Discord delivery", () => {
169169
expect(unresolved.status).toBe("unresolved");
170170
expect(unresolved.retryable).toBe(false);
171171
});
172+
173+
it("keeps ETIMEDOUT unresolved (can be post-dispatch response timeout)", async () => {
174+
const durable = (discordPlugin.message as Record<string, unknown>).durableFinal as
175+
| { reconcileUnknownSend?: Function }
176+
| undefined;
177+
expect(durable?.reconcileUnknownSend).toBeDefined();
178+
const result = await durable!.reconcileUnknownSend!({
179+
cfg: {},
180+
queueId: "test-q",
181+
channel: "discord",
182+
to: "channel:1",
183+
enqueuedAt: Date.now(),
184+
retryCount: 0,
185+
payloads: [{ text: "t" }],
186+
lastError: "fetch failed | ETIMEDOUT",
187+
});
188+
const unresolved = result as { status: string; error: string; retryable: boolean };
189+
expect(unresolved.status).toBe("unresolved");
190+
expect(unresolved.retryable).toBe(false);
191+
});
172192
});

0 commit comments

Comments
 (0)