diff --git a/src/agents/auth-profiles/usage.test.ts b/src/agents/auth-profiles/usage.test.ts index b9ae6cddd8e..b71196d3f5c 100644 --- a/src/agents/auth-profiles/usage.test.ts +++ b/src/agents/auth-profiles/usage.test.ts @@ -1091,6 +1091,19 @@ describe("markAuthProfileFailure — WHAM-aware Codex cooldowns", () => { expect(store.usageStats?.["openai:default"]?.cooldownUntil).toBe(now + 300_000); }); + it("cancels WHAM HTTP error response bodies", async () => { + const now = 1_700_000_000_000; + const store = makeStore({}); + const response = new Response("server busy", { status: 500 }); + const cancel = vi.spyOn(response.body!, "cancel").mockResolvedValue(undefined); + fetchMock.mockResolvedValueOnce(response); + + await markCodexFailureAt({ store, now }); + + expect(cancel).toHaveBeenCalledOnce(); + expect(store.usageStats?.["openai:default"]?.cooldownUntil).toBe(now + 300_000); + }); + it("preserves a longer existing cooldown via max semantics", async () => { const now = 1_700_000_000_000; const existingCooldownUntil = now + 6 * 60 * 60 * 1000; diff --git a/src/agents/auth-profiles/usage.ts b/src/agents/auth-profiles/usage.ts index 2a8da0fc58d..82c7ef5e81a 100644 --- a/src/agents/auth-profiles/usage.ts +++ b/src/agents/auth-profiles/usage.ts @@ -210,6 +210,12 @@ function applyWhamCooldownResult(params: { }; } +async function cancelUnreadResponseBody(response: Response): Promise { + if (!response.bodyUsed) { + await response.body?.cancel().catch(() => undefined); + } +} + async function probeWhamForCooldown( store: AuthProfileStore, profileId: string, @@ -249,6 +255,7 @@ async function probeWhamForCooldown( }); if (!res.ok) { + await cancelUnreadResponseBody(res); if (res.status === 401) { return { cooldownMs: WHAM_TOKEN_EXPIRED_COOLDOWN_MS, reason: "wham_token_expired" }; }