diff --git a/src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts b/src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts index afb60b81d09..37d0f4d80c6 100644 --- a/src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts +++ b/src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts @@ -501,6 +501,20 @@ describe("isFailoverErrorMessage", () => { expect(isFailoverErrorMessage(sample)).toBe(true); } }); + + it("matches Gemini MALFORMED_RESPONSE stop reason as timeout (#42149)", () => { + const samples = [ + "Unhandled stop reason: MALFORMED_RESPONSE", + "Unhandled stop reason: malformed_response", + "Unhandled stop reason: MALFORMED_FUNCTION_CALL", + "stop reason: MALFORMED_RESPONSE", + ]; + for (const sample of samples) { + expect(isTimeoutErrorMessage(sample)).toBe(true); + expect(classifyFailoverReason(sample)).toBe("timeout"); + expect(isFailoverErrorMessage(sample)).toBe(true); + } + }); }); describe("parseImageSizeError", () => { diff --git a/src/agents/pi-embedded-helpers/failover-matches.ts b/src/agents/pi-embedded-helpers/failover-matches.ts index f2e0e3870ab..b61eb4c79b2 100644 --- a/src/agents/pi-embedded-helpers/failover-matches.ts +++ b/src/agents/pi-embedded-helpers/failover-matches.ts @@ -40,9 +40,9 @@ const ERROR_PATTERNS = { /\benotfound\b/i, /\beai_again\b/i, /without sending (?:any )?chunks?/i, - /\bstop reason:\s*(?:abort|error)\b/i, - /\breason:\s*(?:abort|error)\b/i, - /\bunhandled stop reason:\s*(?:abort|error)\b/i, + /\bstop reason:\s*(?:abort|error|malformed_\w+)\b/i, + /\breason:\s*(?:abort|error|malformed_\w+)\b/i, + /\bunhandled stop reason:\s*(?:abort|error|malformed_\w+)\b/i, ], billing: [ /["']?(?:status|code)["']?\s*[:=]\s*402\b|\bhttp\s*402\b|\berror(?:\s+code)?\s*[:=]?\s*402\b|\b(?:got|returned|received)\s+(?:a\s+)?402\b|^\s*402\s+payment/i,