fix(failover): classify finish_reason: network_error as timeout (#61281)

OpenAI-compatible providers (e.g. Z.AI) surface transport errors as
`finish_reason: network_error` in the stream body. The existing
`\breason:` pattern does not match `finish_reason:` because `_` is a
word character, so no word boundary exists before `reason`. This causes
the error to fall through unclassified and skip the timeout-failover
path entirely.

Add an explicit `\bfinish_reason:` pattern that covers the same set of
terminal reasons already handled by the `\breason:` pattern.
This commit is contained in:
chaoliang yan
2026-04-06 18:56:08 +10:00
committed by Altay
parent 7fbd31818b
commit de980c7b97
2 changed files with 16 additions and 0 deletions

View File

@@ -779,6 +779,17 @@ describe("isFailoverErrorMessage", () => {
]);
});
it("matches Provider finish_reason: network_error as timeout (#61281)", () => {
// OpenAI-compatible providers like Z.AI emit this exact format.
// `\breason:` does not match `finish_reason:` because `_` is a word
// character and there is no word boundary before `reason`.
expectTimeoutFailoverSamples([
"Provider finish_reason: network_error",
"Provider finish_reason: abort",
"Provider finish_reason: malformed_response",
]);
});
it("does not classify MALFORMED_FUNCTION_CALL as timeout", () => {
const sample = "Unhandled stop reason: MALFORMED_FUNCTION_CALL";
expect(isTimeoutErrorMessage(sample)).toBe(false);

View File

@@ -114,6 +114,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,
// OpenAI-compatible providers (e.g. Z.AI) surface transport-level errors as
// `finish_reason: network_error` in the stream body. The `\breason:` pattern
// above does NOT match `finish_reason:` because `_` is a word character so
// there is no word boundary before `reason` in `finish_reason` (#61281).
/\bfinish_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).