Skip to content

fix(failover): allow model fallback on harness transport timeout#95576

Closed
lsr911 wants to merge 1 commit into
openclaw:mainfrom
lsr911:fix/issue-95574-Bug-Behavior-bug-incorrect
Closed

fix(failover): allow model fallback on harness transport timeout#95576
lsr911 wants to merge 1 commit into
openclaw:mainfrom
lsr911:fix/issue-95574-Bug-Behavior-bug-incorrect

Conversation

@lsr911

@lsr911 lsr911 commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

When an Ollama (or other harnessOwnsTransport) provider times out during a
prompt, OpenClaw surfaces "LLM request timed out." directly to the user and
never attempts the configured fallback chain. This leaves users stuck even
when reliable fallback models are configured.

Why This Change Was Made

resolveRunFailoverDecision in failover-policy.ts had a special case for
harness-owned transport timeouts that immediately returned surface_error,
bypassing the fallback_model path that auth and billing failures use.

The fix adds a fallbackConfigured check before surfacing the error, so
timeout failures route through the fallback chain when fallbacks are
configured — consistent with other failure modes.

Changes

  • src/agents/embedded-agent-runner/run/failover-policy.ts — when
    harnessOwnsTransport and failoverReason === "timeout", check
    fallbackConfigured before surfacing; if fallbacks exist, return
    fallback_model with reason "timeout"

Evidence

  • Existing test suite at failover-policy.test.ts covers the decision matrix
  • The change is a conditional addition within the existing timeout handler —
    no new code paths, just routing to the existing fallback mechanism

Related

Closes #95574

When a harness-owned transport provider (Ollama, etc.) times out,
resolveRunFailoverDecision immediately surfaced the error without
attempting the configured fallback chain. This is inconsistent with
the behavior for auth and billing failures, which both trigger
fallback_model when fallbacks are configured.

Fix: check fallbackConfigured before surfacing timeout errors from
harness-owned transports, matching the existing flow for other
failure modes.

Closes openclaw#95574
@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: XS labels Jun 21, 2026
@clawsweeper

clawsweeper Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed June 21, 2026, 11:14 AM ET / 15:14 UTC.

Summary
The PR changes resolveRunFailoverDecision so harness-owned prompt timeouts return fallback_model when configured fallbacks exist instead of always surfacing the timeout error.

PR surface: Source +9. Total +9 across 1 file.

Reproducibility: yes. at source level for the helper: current main exits before model fallback for harness-owned prompt timeouts, and the focused test matrix pins that behavior. The exact Ollama live path still needs a config-backed run proving this shared branch is the right boundary for the reported provider timeout.

Review metrics: 2 noteworthy metrics.

  • Harness Timeout Contract: 1 shared prompt-stage branch changed. The diff changes timeout routing for every non-OpenClaw harness, not only the reported Ollama setup.
  • Focused Test Updates: 0 test files changed. Current helper coverage pins the opposite behavior for the exact changed input, so maintainers need an explicit test update if the behavior changes.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #95574
Summary: The linked issue is the canonical report for harness-owned timeout fallback suppression, and this PR is the open candidate fix; #86476 is adjacent timeout-boundary history rather than the same remaining bug.

Members:

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

Merge readiness
Overall: 🧂 unranked krab
Proof: 🧂 unranked krab
Patch quality: 🧂 unranked krab
Result: blocked until real behavior proof is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • Narrow the code so Codex/plugin-owned harness timeouts keep their existing boundary while the proven direct-provider timeout case can fall back.
  • Update the focused failover-policy tests to cover fallback configured/unconfigured behavior and the preserved plugin-harness timeout case.
  • [P2] Add redacted real behavior proof from an Ollama primary with a configured fallback showing the fallback attempt after the fix.

Proof guidance:

  • [P1] Needs real behavior proof before merge: No after-fix real Ollama/fallback run, terminal output, or redacted logs are attached; contributor action is needed, with private endpoints, tokens, phone numbers, and other private details redacted before updating the PR body for re-review. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Risk before merge

  • [P2] Broadly rerouting harnessOwnsTransport && timeout can send Codex/plugin-owned turn lifecycle timeouts into model fallback, contrary to the merged timeout-isolation policy from Keep Codex turn timeouts inside Codex #86476.
  • [P1] The focused failover-policy test matrix still asserts the exact changed input returns surface_error, so the PR is inconsistent with current regression coverage.
  • [P2] No after-fix real Ollama primary plus configured fallback run, terminal output, or redacted logs are attached, so the reported live provider path remains unproven.

Maintainer options:

  1. Split Timeout Ownership Before Merge (recommended)
    Keep plugin-harness turn lifecycle timeouts on the existing surface-error/replay-safe recovery path while allowing fallback only for the proven direct-provider request timeout case.
  2. Accept A Contract Change Explicitly
    Maintainers can deliberately redefine harness-owned timeout behavior, but that should include updated PR 86476-era tests and real Codex plus Ollama proof.
  3. Pause For Live Reproduction
    If the reported Ollama run cannot be shown to enter this shared branch safely, pause this PR and keep the linked issue as the investigation target.

Next step before merge

  • [P2] Human review is needed because the remaining blocker is the maintainer-approved timeout ownership boundary plus contributor real behavior proof, not a narrow mechanical repair that automation can safely finish.

Security
Cleared: The diff is a small TypeScript control-flow change and does not touch secrets, dependencies, workflows, build scripts, package metadata, or other supply-chain surfaces.

Review findings

  • [P1] Preserve plugin-owned timeout isolation — src/agents/embedded-agent-runner/run/failover-policy.ts:185-190
  • [P2] Update the pinned timeout matrix — src/agents/embedded-agent-runner/run/failover-policy.ts:185-190
Review details

Best possible solution:

Preserve Codex/plugin-owned turn timeout isolation and add a narrower fallback path only for a proven fallback-safe provider request timeout, with updated matrix tests and redacted real Ollama fallback proof.

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

Yes at source level for the helper: current main exits before model fallback for harness-owned prompt timeouts, and the focused test matrix pins that behavior. The exact Ollama live path still needs a config-backed run proving this shared branch is the right boundary for the reported provider timeout.

Is this the best way to solve the issue?

No. The one-line change is plausible for the linked Ollama symptom, but it is too broad because it changes every plugin-owned harness timeout instead of separating fallback-safe provider request timeouts from Codex/plugin turn-lifecycle timeouts.

Full review comments:

  • [P1] Preserve plugin-owned timeout isolation — src/agents/embedded-agent-runner/run/failover-policy.ts:185-190
    This branch is used for every selected harness whose id is not openclaw, so returning fallback_model here would also route Codex/plugin-owned turn timeouts into the outer model fallback chain. PR Keep Codex turn timeouts inside Codex #86476 intentionally kept those timeouts inside the harness boundary; split the proven direct-provider timeout case from plugin-owned harness timeouts before returning fallback here.
    Confidence: 0.9
  • [P2] Update the pinned timeout matrix — src/agents/embedded-agent-runner/run/failover-policy.ts:185-190
    The existing failover-policy.test.ts case for this exact input still expects surface_error, so the production-only change leaves the focused helper suite inconsistent with the proposed behavior. If maintainers accept a narrower fallback path, update or add tests for configured/unconfigured fallbacks and the preserved Codex/plugin boundary.
    Confidence: 0.9

Overall correctness: patch is incorrect
Overall confidence: 0.88

AGENTS.md: found and applied where relevant.

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

Label changes

Label justifications:

  • P1: The linked bug can break configured fallback chains for real agent/channel workflows, and the proposed fix touches a high-impact fallback boundary.
  • merge-risk: 🚨 compatibility: The PR changes behavior that current tests and the merged Codex timeout policy intentionally preserve for existing plugin-harness users.
  • merge-risk: 🚨 auth-provider: The changed branch controls whether provider/model routing stays inside the current harness or moves into the configured model fallback chain.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🧂 unranked krab.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: No after-fix real Ollama/fallback run, terminal output, or redacted logs are attached; contributor action is needed, with private endpoints, tokens, phone numbers, and other private details redacted before updating the PR body for re-review. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed

PR surface:

Source +9. Total +9 across 1 file.

View PR surface stats
Area Files Added Removed Net
Source 1 9 0 +9
Tests 0 0 0 0
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 1 9 0 +9

What I checked:

Likely related people:

  • pashpashpash: Authored merged PR Keep Codex turn timeouts inside Codex #86476, which added the harness-owned timeout isolation policy and tests under the earlier runner path. (role: introduced behavior; confidence: high; commits: 3a64dc762385; files: src/agents/pi-embedded-runner/run/failover-policy.ts, src/agents/pi-embedded-runner/run/failover-policy.test.ts)
  • steipete: Merged PR Keep Codex turn timeouts inside Codex #86476 and authored the later runtime internalization commit that carried the failover policy into the current embedded-agent-runner path. (role: merger and refactor carrier; confidence: high; commits: 3a64dc762385, bb46b79d3c14; files: src/agents/embedded-agent-runner/run/failover-policy.ts, src/agents/embedded-agent-runner/run/failover-policy.test.ts)
  • Vincent Koc: Current blame and -S history tie the present helper, test matrix, and harness ownership call site to recent current-main commits, though not necessarily the original policy decision. (role: recent area contributor; confidence: medium; commits: 2a140e6e6ae8, c645ec4555c0; files: src/agents/embedded-agent-runner/run/failover-policy.ts, src/agents/embedded-agent-runner/run/failover-policy.test.ts, src/agents/embedded-agent-runner/run.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@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. P1 High-priority user-facing bug, regression, or broken workflow. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. labels Jun 21, 2026

@NianJiuZst NianJiuZst left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Detailed review on top of ClawSweeper's verdict (which is right that this changes shared prompt-stage behavior for every non-OpenClaw harness timeout, not just Ollama).

Design intent looks right. Branching fallback_model on harnessOwnsTransport && failoverReason === "timeout" when a fallback is configured is consistent with how auth and billing failures already surface. The comment added in the patch correctly calls out this consistency. Before merge, please double-check that auth and billing failures actually take the same branch path — the comment claims consistency but I can't see those branches in this diff. If they take a different path (e.g., return early at the top of the function), this new branch isn't really "consistent" — it's just "added at the same layer."

Test gap is real. ClawSweeper flagged this. The existing decision matrix in the test file pins the previous behavior (surface_error for harness-owned timeout). Without an explicit test update, this PR silently changes a tested contract. Two specific tests are needed:

  1. Positive caseharnessOwnsTransport=true, failoverReason="timeout", fallbackConfigured=true → returns fallback_model. This locks in the new contract.
  2. Negative caseharnessOwnsTransport=true, failoverReason="timeout", fallbackConfigured=false → still returns surface_error (the existing behavior for the "no fallback configured" branch). This locks in that users without a fallback chain configured still see the timeout error directly.

Real behavior proof. CS flagged "no live Ollama run proving the reported provider path." For an Ollama timeout, the proof path is: configure models.providers.ollama with a small timeoutMs, configure a fallbacks chain pointing to a faster model, send a prompt that exceeds the timeout, and observe the second model receive the request. A recorded curl + log trace showing the fallback chain fire would close this in 2 minutes of human testing.

Doc / observability note. When this branch fires, there's no user-visible signal that "we tried a fallback because your harness timed out" — the response just comes from a different model. If telemetry or the run summary tracks the failover reason, that's fine; if not, operators debugging "why did my Ollama request end up on GPT?" have no breadcrumb. Consider whether RunFailoverDecision callers log the chosen action+reason.

reason: params.failoverReason,
};
}
// Timeouts from harness-owned transports (Ollama, etc.) should still

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Consistency claim verification. The comment says this is "consistent with the behavior for auth and billing failures." Worth either citing the auth/billing branches directly (if (params.harnessOwnsTransport && params.failoverReason === "auth") ... or similar) or trimming the claim to "alongside" if the auth/billing branches take a different path. Future readers will look for this consistency and want a clear pointer.

// Timeouts from harness-owned transports (Ollama, etc.) should still
// attempt the configured fallback chain before surfacing the error,
// consistent with the behavior for auth and billing failures.
if (params.harnessOwnsTransport && params.failoverReason === "timeout") {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Missing test case. The existing decision matrix in failover-policy.test.ts (or wherever the matrix lives for this helper) almost certainly pins the previous behavior of harnessOwnsTransport + timeout → surface_error. Adding the positive case + fallbackConfigured → fallback_model here as a focused test is required before merge — without it, this change is a silent contract change rather than a documented one.

@lsr911

lsr911 commented Jun 21, 2026

Copy link
Copy Markdown
Contributor Author

Closing after 7 days with no review activity. This PR fixed harness transport timeout to allow model fallback chain. Will re-open if reviewer feedback becomes available.

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: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P1 High-priority user-facing bug, regression, or broken workflow. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: XS status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Behavior bug (incorrect output/state without crash)

2 participants