Skip to content

fix(agents/failover): classify bare pi-ai stream wrapper as timeout for all providers#71647

Merged
steipete merged 2 commits into
openclaw:mainfrom
willamhou:fix/failover-pi-ai-generic-unknown-error
Apr 25, 2026
Merged

fix(agents/failover): classify bare pi-ai stream wrapper as timeout for all providers#71647
steipete merged 2 commits into
openclaw:mainfrom
willamhou:fix/failover-pi-ai-generic-unknown-error

Conversation

@willamhou

Copy link
Copy Markdown
Contributor

Fixes #71620.

Root cause

@mariozechner/pi-ai providers (anthropic, google, google-vertex, openai-completions, openai-responses, mistral, amazon-bedrock, azure-openai-responses, google-gemini-cli) all throw a literal Error("An unknown error occurred") from a shared stream-wrapper pattern when the stream ends with stopReason: "aborted" | "error" and no specific error info is attached.

OpenClaw classified that bare string as transient (timeout) for failover only when the active provider name contained "anthropic", via isAnthropicGenericUnknownError() in src/agents/pi-embedded-helpers/errors.ts:756. For non-Anthropic primaries (the issue reporter is on google/gemini-2.5-flash) the classifier returned null, so:

  • isFailoverAssistantError()false
  • failoverFailure branch in pi-embedded-runner/run.ts:1301 never fires
  • The configured agents.defaults.model.fallbacks chain is silently bypassed
  • The literal An unknown error occurred text is surfaced to end users

This is provider-agnostic upstream, but the gate was provider-scoped.

Fix

src/agents/pi-embedded-helpers/errors.ts:756: rename isAnthropicGenericUnknownError(raw, provider) to isGenericUnknownStreamError(raw) and drop the isProvider(provider, "anthropic") gate. Replace the includes("an unknown error occurred") substring check with an anchored regex /^\s*an unknown error occurred\.?\s*$/i so we only catch the bare wrapper message and not user-text or assistant prose that happens to contain the phrase.

Tests previously locked the old behavior in two places:

  • pi-embedded-helpers.isbillingerrormessage.test.ts:742classifies provider-scoped generic upstream messages (kept the OpenRouter assertions, dropped the "An unknown error occurred" line because it no longer needs scoping)
  • pi-embedded-helpers.isbillingerrormessage.test.ts:754 and failover-error.test.ts:459does not classify provider-scoped generic upstream messages without provider context (kept the Provider returned error / Key limit exceeded lines, dropped the "An unknown error occurred" lines that were the bug)

Added new tests covering bare/case/whitespace variants across providers anthropic, google, openrouter, plus negative tests for wrapped messages (LLM request failed with an unknown error. and a sentence containing the phrase mid-string) so they keep returning null.

Verification

  • pnpm test src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts src/agents/failover-error.test.ts — 189/189 passed.
  • pnpm test src/plugins/provider-runtime.test.ts — 30/30 passed (uses the same classifier through isFailoverErrorMessage).
  • npx oxlint <changed files> — 0 errors, 0 warnings.
  • pnpm format:check <changed files> — clean.
  • pnpm tsgo:core — same set of pre-existing unrelated errors as on c070509b7f (ui/src/ui/views/agents-*.ts, src/mcp/*, src/media/qr-runtime.ts, src/plugin-sdk/*, src/trajectory/metadata.ts); confirmed by stashing and re-running on clean main.

Test plan

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: S labels Apr 25, 2026
@greptile-apps

greptile-apps Bot commented Apr 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a provider-scoping bug in the failover classifier where only Anthropic-primary agents would correctly rotate fallback chains on Error("An unknown error occurred") — a bare message thrown provider-agnostically by all @mariozechner/pi-ai stream wrappers. The fix renames isAnthropicGenericUnknownError to isGenericUnknownStreamError, drops the isProvider(provider, "anthropic") gate, and replaces the loose includes() substring match with an anchored regex /^\s*an unknown error occurred\.?\s*$/i that catches case/whitespace variants while excluding prose that contains the phrase mid-string.

Confidence Score: 5/5

Safe to merge — targeted one-line logic change with comprehensive regression tests, no security implications.

The fix is minimal and correct: the anchored regex properly isolates the exact bare message from incidental substrings, the old Anthropic-only gate is cleanly removed, and tests cover positive variants (case, whitespace, trailing period), provider-agnostic paths, and negative anchoring cases. No regressions in related test suites reported.

No files require special attention.

Reviews (1): Last reviewed commit: "fix(agents/failover): classify bare pi-a..." | Re-trigger Greptile

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@steipete steipete force-pushed the fix/failover-pi-ai-generic-unknown-error branch from ea00507 to 6af743b Compare April 25, 2026 17:14
@openclaw-barnacle openclaw-barnacle Bot added the docs Improvements or additions to documentation label Apr 25, 2026
@steipete steipete merged commit 8073973 into openclaw:main Apr 25, 2026
66 checks passed
@steipete

Copy link
Copy Markdown
Contributor

Codex review landed via rebase onto main.

  • Local gate: pnpm test src/agents/failover-error.test.ts src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts
  • Local gate: OPENCLAW_OXLINT_SKIP_LOCK=1 OPENCLAW_TEST_HEAVY_CHECK_LOCK_HELD=1 OPENCLAW_TSGO_HEAVY_CHECK_LOCK_HELD=1 pnpm check:changed
  • Docs gate: pnpm check:docs
  • PR CI: exact SHA checks passed before merge.
  • Source SHA: 6af743b
  • Landed SHAs: 4b5c2f9, 8073973

Thanks @willamhou and @mattcproctor.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great

ayesha-aziz123 pushed a commit to ayesha-aziz123/openclaw that referenced this pull request Apr 26, 2026
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
globalcaos pushed a commit to globalcaos/tinkerclaw that referenced this pull request May 13, 2026
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
jameslcowan pushed a commit to jameslcowan/openclaw that referenced this pull request Jun 2, 2026
sablehead pushed a commit to sablehead/openclaw that referenced this pull request Jun 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling docs Improvements or additions to documentation size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Generic "An unknown error occurred" stream errors don't trigger model fallback for non-Anthropic providers

3 participants