diff --git a/src/agents/provider-transport-fetch.test.ts b/src/agents/provider-transport-fetch.test.ts index 1b4c171d02d..53799f922f5 100644 --- a/src/agents/provider-transport-fetch.test.ts +++ b/src/agents/provider-transport-fetch.test.ts @@ -1111,6 +1111,27 @@ describe("buildGuardedModelFetch", () => { expect(response.headers.get("x-should-retry")).toBeNull(); }); + it.each(["0x10", "1e3"])( + "ignores non-decimal OPENCLAW_SDK_RETRY_MAX_WAIT_SECONDS values: %s", + async (value) => { + process.env.OPENCLAW_SDK_RETRY_MAX_WAIT_SECONDS = value; + fetchWithSsrFGuardMock.mockResolvedValue({ + response: new Response(null, { + status: 429, + headers: { "retry-after": "30" }, + }), + finalUrl: "https://api.anthropic.com/v1/messages", + release: vi.fn(async () => undefined), + }); + const response = await buildGuardedModelFetch(anthropicModel)( + "https://api.anthropic.com/v1/messages", + { method: "POST" }, + ); + + expect(response.headers.get("x-should-retry")).toBeNull(); + }, + ); + it("injects x-should-retry:false for terminal 429 responses without retry-after", async () => { fetchWithSsrFGuardMock.mockResolvedValue({ response: new Response("Sorry, you've exceeded your weekly rate limit.", { diff --git a/src/agents/provider-transport-fetch.ts b/src/agents/provider-transport-fetch.ts index e014815d999..ff904028b80 100644 --- a/src/agents/provider-transport-fetch.ts +++ b/src/agents/provider-transport-fetch.ts @@ -33,6 +33,7 @@ import { const DEFAULT_MAX_SDK_RETRY_WAIT_SECONDS = 60; const log = createSubsystemLogger("provider-transport-fetch"); const BLOCKED_EXACT_ORIGIN_TRUST_HOSTNAME_LABELS = new Set(["instance-data"]); +const PLAIN_DECIMAL_NUMBER_RE = /^\d+(?:\.\d+)?$/; function hasReadableSseData(block: string): boolean { const dataLines = block @@ -269,6 +270,10 @@ function resolveMaxSdkRetryWaitSeconds(): number | undefined { return undefined; } + if (!PLAIN_DECIMAL_NUMBER_RE.test(raw)) { + return DEFAULT_MAX_SDK_RETRY_WAIT_SECONDS; + } + const seconds = Number(raw); if (Number.isFinite(seconds) && seconds > 0) { return seconds;