From 3c9587740fff97e048aef5063ef28dcb80a42d60 Mon Sep 17 00:00:00 2001 From: Altay Date: Thu, 16 Apr 2026 19:17:58 +0300 Subject: [PATCH] fix: tighten 402 failover classification --- ...dded-helpers.isbillingerrormessage.test.ts | 3 +++ src/agents/pi-embedded-helpers/errors.ts | 23 ++++++++----------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts b/src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts index ec208b204dd..029550a09f3 100644 --- a/src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts +++ b/src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts @@ -923,6 +923,9 @@ describe("classifyFailoverReasonFromHttpStatus – 402 temporary limits", () => expect(classifyFailoverReasonFromHttpStatus(402, undefined)).toBe("billing"); expect(classifyFailoverReasonFromHttpStatus(402, "")).toBe("billing"); expect(classifyFailoverReasonFromHttpStatus(402, "Payment required")).toBe("billing"); + expect(classifyFailoverReasonFromHttpStatus(402, "402 custom proxy billing failure")).toBe( + "billing", + ); }); it("matches raw 402 wrappers and status-split payloads for the same message", () => { diff --git a/src/agents/pi-embedded-helpers/errors.ts b/src/agents/pi-embedded-helpers/errors.ts index e8718031ff5..5f09b7ffc08 100644 --- a/src/agents/pi-embedded-helpers/errors.ts +++ b/src/agents/pi-embedded-helpers/errors.ts @@ -583,7 +583,7 @@ export function classifyFailoverReasonFromHttpStatus( ? classifyFailoverClassificationFromMessage(message, opts?.provider) : null; return failoverReasonFromClassification( - classifyFailoverClassificationFromHttpStatus(status, message, messageClassification), + classifyFailoverClassificationFromHttpStatus(status, message, messageClassification, status), ); } @@ -591,6 +591,7 @@ function classifyFailoverClassificationFromHttpStatus( status: number | undefined, message: string | undefined, messageClassification: FailoverClassification | null, + explicitStatus: number | undefined, ): FailoverClassification | null { const messageReason = failoverReasonFromClassification(messageClassification); if (typeof status !== "number" || !Number.isFinite(status)) { @@ -604,7 +605,12 @@ function classifyFailoverClassificationFromHttpStatus( const leadingStatus = extractLeadingHttpStatus(message.trim()); if (leadingStatus?.code === 402) { const reasonFrom402Text = classifyFailoverReasonFrom402Text(message); - return reasonFrom402Text ? toReasonClassification(reasonFrom402Text) : messageClassification; + if (reasonFrom402Text) { + return toReasonClassification(reasonFrom402Text); + } + return typeof explicitStatus === "number" + ? toReasonClassification("billing") + : messageClassification; } return toReasonClassification(classify402Message(message)); } @@ -835,6 +841,7 @@ export function classifyFailoverSignal(signal: FailoverSignal): FailoverClassifi inferredStatus, signal.message, messageClassification, + signal.status, ); if (statusClassification) { return statusClassification; @@ -1246,20 +1253,8 @@ export function classifyFailoverReason( raw: string, opts?: { provider?: string }, ): FailoverReason | null { - const trimmed = raw.trim(); - const leadingStatus = extractLeadingHttpStatus(trimmed); - const reasonFrom402Text = - leadingStatus?.code === 402 ? classifyFailoverReasonFrom402Text(trimmed) : null; - if ( - leadingStatus?.code === 402 && - !reasonFrom402Text && - !isHtmlErrorResponse(trimmed, leadingStatus.code) - ) { - return null; - } return failoverReasonFromClassification( classifyFailoverSignal({ - status: leadingStatus?.code, message: raw, provider: opts?.provider, }),