From 627743aa3820b5ce4beb727db8c793325d969cd8 Mon Sep 17 00:00:00 2001 From: ShionElia Date: Wed, 18 Mar 2026 10:05:29 +0000 Subject: [PATCH 1/2] fix: trigger model failover for generic provider error messages When Anthropic returns a generic error like 'An unknown error occurred' without a type field, classifyFailoverReason() now recognizes it as a transient failure and returns 'timeout' to trigger model failover. Also handles other generic error patterns: 'an error occurred', 'internal server error', and 'service unavailable'. Fixes #49706 --- ...mbedded-helpers.isbillingerrormessage.test.ts | 13 +++++++++++++ src/agents/pi-embedded-helpers/errors.ts | 16 ++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts b/src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts index 8c0a0b1994d..141368bf366 100644 --- a/src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts +++ b/src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts @@ -860,4 +860,17 @@ describe("classifyFailoverReason", () => { ), ).toBe("timeout"); }); + it("classifies generic provider errors as timeout", () => { + expect(classifyFailoverReason("An unknown error occurred")).toBe("timeout"); + expect(classifyFailoverReason("An error occurred")).toBe("timeout"); + expect(classifyFailoverReason("Internal server error")).toBe("timeout"); + expect(classifyFailoverReason("Service unavailable")).toBe("timeout"); + // Case-insensitive + expect(classifyFailoverReason("AN UNKNOWN ERROR OCCURRED")).toBe("timeout"); + expect(classifyFailoverReason("an error occurred while processing")).toBe("timeout"); + // Wrapped in provider payload + expect(classifyFailoverReason('{"error":{"message":"An unknown error occurred"}}')).toBe( + "timeout", + ); + }); }); diff --git a/src/agents/pi-embedded-helpers/errors.ts b/src/agents/pi-embedded-helpers/errors.ts index 6e38d831ad9..99adc32149f 100644 --- a/src/agents/pi-embedded-helpers/errors.ts +++ b/src/agents/pi-embedded-helpers/errors.ts @@ -846,6 +846,19 @@ export function isBillingAssistantError(msg: AssistantMessage | undefined): bool return isBillingErrorMessage(msg.errorMessage ?? ""); } +function isGenericProviderError(raw: string): boolean { + if (!raw) { + return false; + } + const lower = raw.toLowerCase(); + return ( + lower.includes("an unknown error occurred") || + lower.includes("an error occurred") || + lower.includes("internal server error") || + lower.includes("service unavailable") + ); +} + function isJsonApiInternalServerError(raw: string): boolean { if (!raw) { return false; @@ -1024,6 +1037,9 @@ export function classifyFailoverReason(raw: string): FailoverReason | null { if (isAuthErrorMessage(raw)) { return "auth"; } + if (isGenericProviderError(raw)) { + return "timeout"; + } return null; } From 4be25a6866e7168c7f8d502ad016ba18822877d6 Mon Sep 17 00:00:00 2001 From: ShionElia Date: Wed, 18 Mar 2026 10:40:45 +0000 Subject: [PATCH 2/2] fix: remove overly broad 'an error occurred' pattern Per Greptile review feedback, the plain 'an error occurred' substring match is too permissive and could trigger false failovers for application-level errors that aren't transient provider failures. Removed this pattern from isGenericProviderError() and updated tests to verify that generic 'an error occurred' alone does NOT match, while specific patterns like 'an unknown error occurred' still do. --- src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts | 5 +++-- src/agents/pi-embedded-helpers/errors.ts | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts b/src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts index 141368bf366..fd78372f2e0 100644 --- a/src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts +++ b/src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts @@ -862,15 +862,16 @@ describe("classifyFailoverReason", () => { }); it("classifies generic provider errors as timeout", () => { expect(classifyFailoverReason("An unknown error occurred")).toBe("timeout"); - expect(classifyFailoverReason("An error occurred")).toBe("timeout"); expect(classifyFailoverReason("Internal server error")).toBe("timeout"); expect(classifyFailoverReason("Service unavailable")).toBe("timeout"); // Case-insensitive expect(classifyFailoverReason("AN UNKNOWN ERROR OCCURRED")).toBe("timeout"); - expect(classifyFailoverReason("an error occurred while processing")).toBe("timeout"); // Wrapped in provider payload expect(classifyFailoverReason('{"error":{"message":"An unknown error occurred"}}')).toBe( "timeout", ); + // "an error occurred" alone should NOT match (too broad) + expect(classifyFailoverReason("An error occurred")).toBeNull(); + expect(classifyFailoverReason("A validation error occurred")).toBeNull(); }); }); diff --git a/src/agents/pi-embedded-helpers/errors.ts b/src/agents/pi-embedded-helpers/errors.ts index 99adc32149f..1b737fd4775 100644 --- a/src/agents/pi-embedded-helpers/errors.ts +++ b/src/agents/pi-embedded-helpers/errors.ts @@ -853,7 +853,6 @@ function isGenericProviderError(raw: string): boolean { const lower = raw.toLowerCase(); return ( lower.includes("an unknown error occurred") || - lower.includes("an error occurred") || lower.includes("internal server error") || lower.includes("service unavailable") );