From de129a6530c0093a5f4abb0f28dab652b940390a Mon Sep 17 00:00:00 2001 From: Ayaan Zaidi Date: Thu, 16 Apr 2026 18:33:27 +0530 Subject: [PATCH] fix: restrict HTML timeout short-circuit to transient statuses --- ...bedded-helpers.formatassistanterrortext.test.ts | 2 +- src/agents/pi-embedded-helpers/errors.ts | 6 +++++- .../provider-error-patterns.test.ts | 14 ++++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/agents/pi-embedded-helpers.formatassistanterrortext.test.ts b/src/agents/pi-embedded-helpers.formatassistanterrortext.test.ts index 070cae1ce45..5180fafa8d0 100644 --- a/src/agents/pi-embedded-helpers.formatassistanterrortext.test.ts +++ b/src/agents/pi-embedded-helpers.formatassistanterrortext.test.ts @@ -176,7 +176,7 @@ describe("formatAssistantErrorText", () => { ); }); - it("returns upstream HTML copy for prefixed HTML rate-limit pages", () => { + it("returns upstream HTML copy for prefixed 521 HTML rate-limit pages", () => { const msg = makeAssistantError( "Error: 521 rate limit", ); diff --git a/src/agents/pi-embedded-helpers/errors.ts b/src/agents/pi-embedded-helpers/errors.ts index d97f3af6908..1b065df245d 100644 --- a/src/agents/pi-embedded-helpers/errors.ts +++ b/src/agents/pi-embedded-helpers/errors.ts @@ -354,7 +354,11 @@ function isHtmlErrorResponse(raw: string, status?: number): boolean { } function isTransportHtmlErrorStatus(status: number | undefined): boolean { - return status !== 401 && status !== 403 && status !== 407; + return ( + status === 408 || + status === 499 || + (typeof status === "number" && status >= 500 && status < 600) + ); } function isOpenAICodexScopeContext(raw: string, provider?: string): boolean { diff --git a/src/agents/pi-embedded-helpers/provider-error-patterns.test.ts b/src/agents/pi-embedded-helpers/provider-error-patterns.test.ts index 0545470f6c7..8024ce4dd3a 100644 --- a/src/agents/pi-embedded-helpers/provider-error-patterns.test.ts +++ b/src/agents/pi-embedded-helpers/provider-error-patterns.test.ts @@ -167,6 +167,12 @@ describe("Cloudflare / CDN HTML error page classification (#67517)", () => { const html407 = "407 Proxy Authentication Required" + "

Proxy Authentication Required

"; + const html402 = + "402 Payment Required" + + "

Payment Required

Your quota is exhausted.

"; + const html429 = + "429 Too Many Requests" + + "

Too Many Requests

Rate limit exceeded.

"; const prefixedHtml401 = `Error: 401 ${html401}`; const prefixedHtml407 = `Error: 407 ${html407}`; @@ -190,6 +196,14 @@ describe("Cloudflare / CDN HTML error page classification (#67517)", () => { expect(classifyFailoverReason(prefixedHtml401)).toBe("auth"); }); + it("preserves billing classification for 402 HTML", () => { + expect(classifyFailoverReason(`402 ${html402}`)).toBe("billing"); + }); + + it("preserves rate-limit classification for 429 HTML", () => { + expect(classifyFailoverReason(`429 ${html429}`)).toBe("rate_limit"); + }); + it("classifies runtime failure kind as upstream_html for non-auth HTML", () => { expect(classifyProviderRuntimeFailureKind({ status: 502, message: cloudflareHtml502 })).toBe( "upstream_html",