Skip to content

Commit 965cf31

Browse files
author
Kevin Shenghui
committed
fix(discord): add retry logic for chunked message sends on rate-limit
- Add sendWithRetry() wrapper around bot sender path - Retry up to 2 times for 429 and 5xx errors - Support retry-after header for backoff - Fixes issue #32887
1 parent ccee74e commit 965cf31

File tree

1 file changed

+36
-6
lines changed

1 file changed

+36
-6
lines changed

src/discord/monitor/reply-delivery.ts

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,34 @@ function resolveBindingPersona(binding: DiscordThreadBindingLookupRecord | undef
7373
return { username, avatarUrl };
7474
}
7575

76+
const RETRY_ATTEMPTS = 2;
77+
const RETRY_BASE_DELAY_MS = 1000;
78+
79+
async function sendWithRetry(fn: () => Promise<unknown>): Promise<void> {
80+
for (let attempt = 0; attempt <= RETRY_ATTEMPTS; attempt++) {
81+
try {
82+
await fn();
83+
return;
84+
} catch (err: unknown) {
85+
const isLast = attempt === RETRY_ATTEMPTS;
86+
if (isLast) {
87+
throw err;
88+
}
89+
const status =
90+
(err as { status?: number }).status ?? (err as { statusCode?: number }).statusCode;
91+
if (status === 429 || (status !== undefined && status >= 500)) {
92+
const retryAfterMs =
93+
Number((err as { headers?: Record<string, string> }).headers?.["retry-after"]) * 1000 ||
94+
0;
95+
const delayMs = Math.max(retryAfterMs, RETRY_BASE_DELAY_MS * (attempt + 1));
96+
await new Promise((resolve) => setTimeout(resolve, delayMs));
97+
continue;
98+
}
99+
throw err;
100+
}
101+
}
102+
}
103+
76104
async function sendDiscordChunkWithFallback(params: {
77105
target: string;
78106
text: string;
@@ -105,12 +133,14 @@ async function sendDiscordChunkWithFallback(params: {
105133
// Fall through to the standard bot sender path.
106134
}
107135
}
108-
await sendMessageDiscord(params.target, text, {
109-
token: params.token,
110-
rest: params.rest,
111-
accountId: params.accountId,
112-
replyTo: params.replyTo,
113-
});
136+
await sendWithRetry(() =>
137+
sendMessageDiscord(params.target, text, {
138+
token: params.token,
139+
rest: params.rest,
140+
accountId: params.accountId,
141+
replyTo: params.replyTo,
142+
}),
143+
);
114144
}
115145

116146
async function sendAdditionalDiscordMedia(params: {

0 commit comments

Comments
 (0)