failover: classify AbortError / stream-abort messages as timeout (#58315) (#58324)

Merged via squash.

Prepared head SHA: d8412f27e6
Co-authored-by: yelog <14227866+yelog@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Reviewed-by: @altaywtf
This commit is contained in:
Logan Ye
2026-04-02 05:02:31 +08:00
committed by GitHub
parent 75ab5bce6b
commit d9a7ffe003
3 changed files with 18 additions and 0 deletions

View File

@@ -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",

View File

@@ -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,