diff --git a/src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts b/src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts index 8c0a0b1994d..b4671f3643d 100644 --- a/src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts +++ b/src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts @@ -585,6 +585,20 @@ describe("isFailoverErrorMessage", () => { ]); }); + it("matches gateway/websocket connection failures as timeout failover", () => { + const samples = [ + "Connection error.", + "gateway not connected", + "gateway closed (1006): abnormal closure (no close frame)", + "websocket disconnected while waiting for response", + ]; + for (const sample of samples) { + expect(isTimeoutErrorMessage(sample)).toBe(true); + expect(classifyFailoverReason(sample)).toBe("timeout"); + expect(isFailoverErrorMessage(sample)).toBe(true); + } + }); + it("matches Gemini MALFORMED_RESPONSE stop reason as timeout (#42149)", () => { expectTimeoutFailoverSamples([ "Unhandled stop reason: MALFORMED_RESPONSE", diff --git a/src/agents/pi-embedded-helpers/failover-matches.ts b/src/agents/pi-embedded-helpers/failover-matches.ts index 9f6e83e9461..b521a2ab582 100644 --- a/src/agents/pi-embedded-helpers/failover-matches.ts +++ b/src/agents/pi-embedded-helpers/failover-matches.ts @@ -32,6 +32,7 @@ const ERROR_PATTERNS = { "deadline exceeded", "context deadline exceeded", "connection error", + "gateway not connected", "network error", "network request failed", "fetch failed", @@ -47,6 +48,8 @@ const ERROR_PATTERNS = { /\benotfound\b/i, /\beai_again\b/i, /without sending (?:any )?chunks?/i, + /\bwebsocket\b.*\b(?:close|closed|disconnect|disconnected|timeout|timed out)\b/i, + /\bgateway closed\s*\((?:1006|1012|4000)\b/i, /\bstop reason:\s*(?:abort|error|malformed_response|network_error)\b/i, /\breason:\s*(?:abort|error|malformed_response|network_error)\b/i, /\bunhandled stop reason:\s*(?:abort|error|malformed_response|network_error)\b/i,