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

Merged via squash.

Prepared head SHA: f4ab2f9e0b
Co-authored-by: lawrence3699 <247479654+lawrence3699@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Reviewed-by: @altaywtf
This commit is contained in:
chaoliang yan
2026-04-15 03:42:16 +10:00
committed by GitHub
parent 7fbd31818b
commit e0d1810632
3 changed files with 11 additions and 0 deletions

View File

@@ -21,6 +21,7 @@ Docs: https://docs.openclaw.ai
- Agents/compaction: cap the compaction reserve-token floor to the model context window so small-context local models (e.g. Ollama with 16K tokens) no longer trigger context-overflow errors or infinite compaction loops on every prompt. (#65671) Thanks @openperf.
- Agents/OpenAI Responses: classify the exact `Unknown error (no error details in response)` transport failure as failover reason `unknown` so assistant/model fallback still runs for that no-details failure path. (#65254) Thanks @OpenCodeEngineer.
- Models/probe: surface invalid-model probe failures as `format` instead of `unknown` in `models list --probe`, and lock the invalid-model fallback path in with regression coverage. (#50028) Thanks @xiwuqi.
- Agents/failover: classify OpenAI-compatible `finish_reason: network_error` stream failures as timeout so model fallback retries continue instead of stopping with an unknown failover reason. (#61784) thanks @lawrence3699.
## 2026.4.14

View File

@@ -779,6 +779,14 @@ describe("isFailoverErrorMessage", () => {
]);
});
it("matches Provider finish_reason: network_error as timeout (#61281)", () => {
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,8 @@ 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,
// `\breason:` does not match provider payloads like `finish_reason: network_error` (#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).