From 1ed6e70b96c5b0de5df9a50cb0aa8d3fbcb2cc4b Mon Sep 17 00:00:00 2001 From: yelog Date: Tue, 31 Mar 2026 19:02:49 +0800 Subject: [PATCH] failover: classify AbortError and stream-abort messages as timeout instead of unknown (#58315) --- ...pi-embedded-helpers.isbillingerrormessage.test.ts | 12 ++++++++++++ src/agents/pi-embedded-helpers/failover-matches.ts | 5 +++++ 2 files changed, 17 insertions(+) diff --git a/src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts b/src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts index eb16f20d49a..dabe78c2d26 100644 --- a/src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts +++ b/src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts @@ -656,6 +656,18 @@ describe("isFailoverErrorMessage", () => { ]); }); + it("matches AbortError / stream-abort messages as timeout (#58315)", () => { + expectTimeoutFailoverSamples([ + "The operation was aborted", + "This operation was aborted", + "the operation was aborted", + "stream closed", + "stream was closed", + "stream aborted", + "stream was aborted", + ]); + }); + 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 405a31a81e8..1b3cb262e15 100644 --- a/src/agents/pi-embedded-helpers/failover-matches.ts +++ b/src/agents/pi-embedded-helpers/failover-matches.ts @@ -63,6 +63,11 @@ const ERROR_PATTERNS = { /\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, + // AbortError messages from fetch/stream aborts (Ollama NDJSON stream + // timeouts, signal aborts, etc.) — without these the flattened message + // falls through to reason=unknown (#58315). + /\boperation was aborted\b/i, + /\bstream (?:was )?(?:closed|aborted)\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,