Skip to content

fix: run fallback models on upstream provider errors#96196

Closed
markoub wants to merge 1 commit into
openclaw:mainfrom
markoub:markoub/provider-upstream-error-fallback
Closed

fix: run fallback models on upstream provider errors#96196
markoub wants to merge 1 commit into
openclaw:mainfrom
markoub:markoub/provider-upstream-error-fallback

Conversation

@markoub

@markoub markoub commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Fixes #95519.

What Problem This Solves

Fixes an issue where users with configured fallback models could still see a primary provider failure instead of trying the next model when the primary provider returned a transient upstream error payload such as upstream_error / LLM request failed.

Why This Change Was Made

Embedded agent result fallback already treats auth, billing, and rate-limit error payloads as safe fallback triggers when no user-visible output was delivered. This extends that same guarded path to transient provider failures (overloaded, server_error, and timeout) and teaches the shared matcher the canonical upstream_error token, while leaving schema/tool-payload rejections non-retryable.

User Impact

Users with model fallbacks configured should get an automatic fallback attempt for transient upstream provider failures instead of a failed turn from the first provider. Request-shape errors and already-delivered user-visible output remain unchanged.

Evidence

Real behavior proof: A local OpenClaw production-classifier/runtime proof command imported the production classifyEmbeddedAgentRunResultForModelFallback and runWithModelFallback modules, fed the provider error payload shape from #95519, and verified the runtime advanced from the primary model to the configured fallback model. The proof used a temporary redacted OPENCLAW_STATE_DIR; no credentials or external provider calls were used.

Redacted terminal output from the proof command:

[model-fallback/decision] model fallback decision: decision=candidate_failed requested=openai/gpt-5.5 candidate=openai/gpt-5.5 reason=timeout providerErrorType=upstream_error next=anthropic/claude-sonnet-4-6 detail=Upstream request failed
[model-fallback/decision] model fallback decision: decision=candidate_succeeded requested=openai/gpt-5.5 candidate=anthropic/claude-sonnet-4-6 reason=unknown next=none
{
  "proof": "OpenClaw #96196 production fallback classifier and fallback runtime",
  "tempState": "<redacted temp OPENCLAW_STATE_DIR>",
  "upstreamPayloadText": "{\"error\":{\"message\":\"Upstream request failed\",\"type\":\"upstream_error\",\"param\":\"\",\"code\":null}}",
  "productionClassification": {
    "message": "openai/gpt-5.5 ended with a provider error: {\"error\":{\"message\":\"Upstream request failed\",\"type\":\"upstream_error\",\"param\":\"\",\"code\":null}}",
    "reason": "timeout",
    "code": "embedded_error_payload",
    "rawError": "{\"error\":{\"message\":\"Upstream request failed\",\"type\":\"upstream_error\",\"param\":\"\",\"code\":null}}"
  },
  "schemaRejectionClassification": null,
  "runtime": {
    "outcome": "completed",
    "provider": "anthropic",
    "model": "claude-sonnet-4-6",
    "attempts": [
      {
        "provider": "openai",
        "model": "gpt-5.5",
        "error": "{\"error\":{\"message\":\"Upstream request failed\",\"type\":\"upstream_error\",\"param\":\"\",\"code\":null}}",
        "reason": "timeout",
        "code": "embedded_error_payload"
      }
    ],
    "callSequence": [
      {
        "provider": "openai",
        "model": "gpt-5.5",
        "isFinalFallbackAttempt": false
      },
      {
        "provider": "anthropic",
        "model": "claude-sonnet-4-6",
        "isFinalFallbackAttempt": true
      }
    ],
    "fallbackPayloadText": "fallback model completed the turn"
  },
  "assertions": {
    "upstreamErrorClassifiedAsFallback": true,
    "schemaRejectionNotRetried": true,
    "fallbackAttempted": true,
    "completedWithFallback": true
  }
}

Focused validation:

  • node scripts/run-vitest.mjs src/agents/embedded-agent-runner/result-fallback-classifier.test.ts
  • node scripts/run-vitest.mjs src/agents/embedded-agent-runner/result-fallback-classifier.test.ts src/agents/outcome-fallback-runtime-contract.test.ts src/agents/model-fallback.test.ts src/agents/embedded-agent-helpers.isbillingerrormessage.test.ts
  • pnpm exec oxfmt --check src/agents/embedded-agent-helpers/failover-matches.ts src/agents/embedded-agent-runner/result-fallback-classifier.ts src/agents/embedded-agent-runner/result-fallback-classifier.test.ts
  • node scripts/run-oxlint.mjs src/agents/embedded-agent-helpers/failover-matches.ts src/agents/embedded-agent-runner/result-fallback-classifier.ts src/agents/embedded-agent-runner/result-fallback-classifier.test.ts
  • pnpm tsgo:core:test
  • git diff --check
  • codex review --base origin/main -> no discrete correctness regressions found

AI-assisted contribution disclosure: I used Codex to inspect the fallback/classifier path, implement the patch, run focused validation, produce the production proof above, and prepare this PR. I reviewed the diff and validation output before submission.

@clawsweeper

clawsweeper Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Thanks for the context here. I swept through the related work, and this is now duplicate or superseded.

Close: this PR is a plausible and proof-backed candidate, but the same remaining bug is better owned by the open, mergeable, proof-positive canonical PR at #95542.

Root-cause cluster
Relationship: superseded
Canonical: #95542
Summary: This PR and the canonical PR target the same embedded provider upstream_error fallback bug; the canonical PR is the safer proof-positive landing path.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Canonical path: Close this PR and let #95542 be the canonical landing path for the linked upstream_error fallback bug.

So I’m closing this here and keeping the remaining discussion on #95542.

Review details

Best possible solution:

Close this PR and let #95542 be the canonical landing path for the linked upstream_error fallback bug.

Do we have a high-confidence way to reproduce the issue?

Yes, at source level: current main can be traced from embedded provider error payload classification to a null classifier result, which runWithModelFallback treats as a completed primary attempt instead of advancing fallback.

Is this the best way to solve the issue?

No, not as the best landing path: this patch is plausible, but #95542 is the stronger canonical fix because it handles structured upstream_error normalization at the shared boundary and is open, mergeable, and proof-positive.

Security review:

Security review cleared: Security review cleared: the diff changes internal TypeScript fallback classification and tests only, with no dependency, workflow, package, secret, or code-execution surface change.

AGENTS.md: found and applied where relevant.

What I checked:

Likely related people:

  • steipete: Recent current-main commits define the result-fallback classifier, model-fallback behavior, and model-failover docs that govern this path. (role: recent fallback area contributor; confidence: high; commits: 0314819f918a, 6719528316c0, bb46b79d3c14; files: src/agents/embedded-agent-runner/result-fallback-classifier.ts, src/agents/model-fallback.ts, docs/concepts/model-failover.md)
  • vincentkoc: Recent commits touched shared error normalization and model-fallback behavior adjacent to provider/model fallback routing. (role: recent adjacent fallback contributor; confidence: medium; commits: 80805ad7a583, aa3797c8d0d7, 9fd9aa5fcdd2; files: src/agents/model-fallback.ts, src/agents/embedded-agent-helpers/errors.ts)
  • jason-allen-oneal: Recent current-main history touched result-fallback classification and raw provider error presentation boundaries relevant to this upstream payload path. (role: adjacent error-boundary contributor; confidence: medium; commits: b8f1961aaeba, 01f6ad605615; files: src/agents/embedded-agent-runner/result-fallback-classifier.ts, src/agents/embedded-agent-helpers/errors.ts)
  • takhoffman: The earlier embedded provider business-denial fallback classifier merge records takhoffman approval/co-author context for this same classifier boundary. (role: adjacent reviewer/co-author context; confidence: medium; commits: 18f94fc83a72; files: src/agents/embedded-agent-runner/result-fallback-classifier.ts)

Codex review notes: model internal, reasoning high; reviewed against ae06d846faf3.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. labels Jun 24, 2026
@markoub

markoub commented Jun 24, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 24, 2026
@clawsweeper

clawsweeper Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper applied the proposed close for this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: S status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Fallback should trigger on provider upstream_error / LLM request failed

1 participant