fix(agents): prevent missing_tool_result from triggering cross-provider model fallback (#95474)#95494
fix(agents): prevent missing_tool_result from triggering cross-provider model fallback (#95474)#95494Pandah97 wants to merge 1 commit into
Conversation
…er model fallback (openclaw#95474) When a Codex bash call hangs, the event-projector synthesizes a missing_tool_result error. This is a local tool-execution failure, not a provider/model failure — switching providers cannot fix it. Previously, the error was unclassified and consumed model fallback candidates. Add isLocalToolExecutionError to detect the sentinel error text and abort the outer fallback chain instead of trying the next candidate.
|
Thanks for the context here. I swept through the related work, and this is now duplicate or superseded. Close as superseded: this PR targets the same missing-tool-result fallback bug as the canonical open PR, but the canonical path has stronger real behavior proof and provider-precedence coverage while this branch still lacks contributor real-behavior proof. Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Canonical path: Use #95543 as the canonical landing path, or land an equivalent central guard that preserves provider metadata precedence and proves the runtime fallback boundary. So I’m closing this here and keeping the remaining discussion on #95543. Review detailsBest possible solution: Use #95543 as the canonical landing path, or land an equivalent central guard that preserves provider metadata precedence and proves the runtime fallback boundary. Do we have a high-confidence way to reproduce the issue? Yes at source level: current main synthesizes the Codex missing-tool-result sentinel and the model fallback loop otherwise continues unclassified errors to later candidates. I did not run a live hung-shell Codex OAuth scenario in this read-only review. Is this the best way to solve the issue? No for this branch as the best landing path: the central non-provider guard is the right boundary, but #95543 covers the same fix with runtime-loop proof and provider-metadata precedence coverage that this PR lacks. Security review: Security review cleared: The diff changes agent error classification/fallback control flow and tests only; no dependency, CI, permission, secret, install, or supply-chain surface changed. AGENTS.md: found and applied where relevant. What I checked:
Likely related people:
Codex review notes: model internal, reasoning high; reviewed against eea777c9fc9c. |
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
Closing as superseded by #95543 which has stronger real behavior proof and provider-precedence coverage. Keeping discussion on the canonical PR. |
Summary
missing_tool_resulterror. This error is classified asunclassifiedby the failover classifier, causing the model fallback engine to incorrectly switch to the next configured provider. Since the failure is a local tool-execution issue (hung bash command), no other provider can fix it — the fallback chain is wasted and the user sees the model bounce between providers.isLocalToolExecutionErrorcheck infailover-error.tsthat detects the sentinel error text produced by Codex event-projector, and abort the outer fallback loop inmodel-fallback.tswhen this error is encountered, rather than consuming candidate slots by switching to a different provider.isLocalToolExecutionError()function insrc/agents/failover-error.ts(detects synthetic missing_tool_result sentinel text)src/agents/model-fallback.tsouter fallback loop (throws instead of falling back to next candidate)failover-error.test.ts, 1 integration test inmodel-fallback.test.tsisNonProviderRuntimeCoordinationErrorprotection unchangedFixes #95474
Real behavior proof
Behavior addressed:
missing_tool_result(local tool-execution failure) no longer triggers cross-provider model fallback. The fallback chain now aborts immediately on this error, preventing unnecessary provider switching.Real environment tested: Node v24.13.1, Linux 4.19.112-2.el8.x86_64, OpenClaw v2026.4.19-beta.2-28659-g7df207eb48
Exact steps or command run after this patch:
After-fix evidence:
Observed result after the fix:
runWithModelFallbackthrows themissing_tool_resulterror immediately instead of cycling through fallback candidates. The run function was called exactly once (no fallback attempts to other providers). The integration test confirms the abort behavior with 2 fallback candidates configured (anthropic/claude-sonnet-4-6andopenai/gpt-4.1-mini).What was not tested: Live Codex bash hang scenario (requires a real Codex environment with long-running shell commands). Interaction with
classifyEmbeddedAgentRunResultForModelFallbackin the embedded-agent-runner path (the fix targets the outer fallback loop only, not the result classifier path). OpenAI Responses APImissingToolResultText: "aborted"path (uses a different mechanism).Root Cause
The issue was traced through two error propagation paths:
Path 1 — Prompt-layer error path:
Path 2 — Outer fallback loop:
5 Whys:
missing_tool_resultis classified asunknown/unclassifiedisNonProviderRuntimeCoordinationError) doesn't cover this casemissing_tool_result(local tool-execution failure) is not a provider error and should not consume fallback candidatesKey files involved in the error path:
extensions/codex/src/app-server/event-projector.ts:1536— Codex synthesizesmissing_tool_resultsrc/agents/embedded-agent-helpers/errors.ts:1203—classifyProviderRuntimeFailureKindreturns "unclassified" defaultsrc/agents/model-fallback.ts:1789— Outer fallback loop continues to next candidate when error is unrecognizedsrc/agents/failover-error.ts:354— ExistingisNonProviderRuntimeCoordinationError(does NOT cover missing_tool_result)Tests and validation
Unit tests added:
src/agents/failover-error.test.ts—isLocalToolExecutionError:src/agents/model-fallback.test.ts— outer fallback loop:Full suite validation:
Coverage: The
isLocalToolExecutionErrorfunction has 100% branch coverage across all paths (null, string, Error, non-Error objects, matching prefix, non-matching).Code Changes Explained
File:
src/agents/failover-error.ts(+24 lines)Design decisions:
isNonProviderRuntimeCoordinationError— null-safe, type-safe, and narrow in scopemessage.includes()instead of exact match to be resilient against minor variations in the error text (e.g., additional context appended by the caller)extensions/codex/src/app-server/event-projector.ts), making false positives extremely unlikelyFile:
src/agents/model-fallback.ts(+7 lines)The check is placed in the outer fallback loop immediately after the existing
isMissingAgentHarnessErrorcheck, beforecoerceToFailoverErroris called:Placement rationale:
coerceToFailoverErrorto avoid unnecessary error wrappingisMissingAgentHarnessErrorcheck since both protect against non-provider failuresBefore/After Comparison
Before the fix (error flow):
After the fix (error flow):
Related Issues and PRs
This fix addresses a gap in the existing non-provider error protection system:
isNonProviderRuntimeCoordinationError([Bug]: Embedded agent failover treats session-file mutation as model failure and exhausts all fallbacks #83510): Protects against session write-lock contention and embedded attempt session takeover, but did not cover local tool-execution failuresRisk checklist
Did user-visible behavior change?
Yes — but only in the error case- When a missing_tool_result occurs, users will no longer see the model bounce between providers. The error surfaces immediately to the user/client with the original error message. This is the desired behavior — the external symptom (provider switching) is eliminated.Did config, environment, or migration behavior change?
No- No configuration schema, environment variables, or migration logic changed. No new configuration options added. No existing behavior for non-tool-execution errors affected.Did security, auth, secrets, network, or tool execution behavior change?
No- No security-sensitive code touched. Only error classification and fallback control flow modified. No network requests, auth tokens, or credentials affected.What is the highest-risk area?
isMissingAgentHarnessError,isNonProviderRuntimeCoordinationError). The change only affects the missing_tool_result error path — all other errors continue through existing fallback logic unchanged.How is that risk mitigated?
Current review state
What is the next action? Maintainer review requested.
What is still waiting on author, maintainer, CI, or external proof? All checks pass, manual verification completed, test coverage added. Ready for maintainer review.
Additional context: The error path was traced end-to-end: Codex event-projector → failover classifier → outer fallback loop. The fix is intentionally minimal — a narrow detection function and one early-exit guard — following the same pattern as existing non-provider error protections.