fix(discord): bound delivery retry delays

This commit is contained in:
Peter Steinberger
2026-05-29 11:02:31 -04:00
parent e394e0f9b8
commit 3d7df2bc07
2 changed files with 26 additions and 14 deletions

View File

@@ -6,6 +6,7 @@ import {
} from "openclaw/plugin-sdk/retry-runtime";
import { resolveDiscordAccount } from "./accounts.js";
import { DiscordError } from "./internal/discord.js";
import { parseDiscordRetryAfterBodySeconds } from "./retry-after.js";
const DISCORD_DELIVERY_RETRY_DEFAULTS = {
attempts: 3,
@@ -22,27 +23,21 @@ export function isRetryableDiscordDeliveryError(err: unknown): boolean {
return status === 429 || (status !== undefined && status >= 500);
}
function getDiscordDeliveryRetryAfterMs(err: unknown): number | undefined {
export function getDiscordDeliveryRetryAfterMs(err: unknown): number | undefined {
if (!err || typeof err !== "object") {
return undefined;
}
if (
"retryAfter" in err &&
typeof err.retryAfter === "number" &&
Number.isFinite(err.retryAfter)
) {
return err.retryAfter * 1000;
const retryAfterSeconds =
"retryAfter" in err ? parseDiscordRetryAfterBodySeconds(err.retryAfter) : undefined;
if (retryAfterSeconds !== undefined) {
return retryAfterSeconds * 1000;
}
const retryAfterRaw = (err as { headers?: Record<string, string> }).headers?.["retry-after"];
if (!retryAfterRaw) {
return undefined;
}
const trimmedRetryAfter = retryAfterRaw.trim();
if (!/^\d+(?:\.\d+)?$/.test(trimmedRetryAfter)) {
return undefined;
}
const retryAfterMs = Number(trimmedRetryAfter) * 1000;
return Number.isFinite(retryAfterMs) ? retryAfterMs : undefined;
const headerSeconds = parseDiscordRetryAfterBodySeconds(retryAfterRaw);
return headerSeconds === undefined ? undefined : headerSeconds * 1000;
}
export async function withDiscordDeliveryRetry<T>(params: {

View File

@@ -1,5 +1,8 @@
import { describe, expect, it, vi } from "vitest";
import { isRetryableDiscordDeliveryError } from "./delivery-retry.js";
import {
getDiscordDeliveryRetryAfterMs,
isRetryableDiscordDeliveryError,
} from "./delivery-retry.js";
import { DiscordError, RateLimitError } from "./internal/discord.js";
import { createDiscordRetryRunner, isRetryableDiscordTransientError } from "./retry.js";
@@ -86,3 +89,17 @@ describe("isRetryableDiscordDeliveryError", () => {
expect(isRetryableDiscordDeliveryError(err)).toBe(false);
});
});
describe("getDiscordDeliveryRetryAfterMs", () => {
it("reads finite retry delays from delivery errors", () => {
expect(getDiscordDeliveryRetryAfterMs({ retryAfter: 0.25 })).toBe(250);
expect(getDiscordDeliveryRetryAfterMs({ headers: { "retry-after": "0.25" } })).toBe(250);
});
it("rejects unsafe retry delay magnitudes", () => {
expect(getDiscordDeliveryRetryAfterMs({ retryAfter: 9_007_199_254_741 })).toBeUndefined();
expect(
getDiscordDeliveryRetryAfterMs({ headers: { "retry-after": "9007199254741" } }),
).toBeUndefined();
});
});