fix #95474: [Bug]: missing_tool_result (local tool-execution failure) is classified unclassified and triggers cross-provider model fallback#95543
Conversation
|
Codex review: needs maintainer review before merge. Reviewed June 27, 2026, 11:46 AM ET / 15:46 UTC. Summary PR surface: Source +52, Tests +89. Total +141 across 3 files. Reproducibility: yes. at source level: current main synthesizes the Codex missing_tool_result sentinel and the fallback loop advances unrecognized thrown errors while candidates remain. I did not run a live long-hanging Codex shell turn in this read-only review. Review metrics: 1 noteworthy metric.
Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land this narrow failover-boundary classifier if maintainers accept it as the canonical fix, then close the linked issue and leave broader native-tool timeout work as separate follow-up if needed. 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 fallback loop advances unrecognized thrown errors while candidates remain. I did not run a live long-hanging Codex shell turn in this read-only review. Is this the best way to solve the issue? Yes, pending maintainer acceptance: extending the existing non-provider runtime guard is the narrowest owner-boundary fix because that guard already runs before fallback candidates are consumed. Adding a new public failover reason or patching downstream loops would be broader and more compatibility-sensitive. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against ddedf13190c1. Label changesLabel justifications:
Evidence reviewedPR surface: Source +52, Tests +89. Total +141 across 3 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
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
|
|
Complementary observations on top of ClawSweeper's review: This targets #95474 (
|
6c88af6 to
d6fe914
Compare
d6fe914 to
baecd4a
Compare
|
Merged via squash.
|
(cherry picked from commit b9c6414)
Summary
Fixes #95474.
missing_tool_result/ "native Codex tool.call without a matching tool.result" failures as local non-provider runtime/tool-execution failures.Maintainer checklist
missing_tool_resultprovider-jump behavior.missing_tool_resulterrors were local tool-execution failures, but the model failover error boundary did not recognize that source invariant, so unknown-error fallback advanced to another provider.CI=1 node scripts/run-vitest.mjs --reporter=basic src/agents/model-fallback.test.ts src/agents/failover-error.test.ts src/agents/embedded-agent-runner/run/failover-policy.test.ts src/agents/embedded-agent-runner/result-fallback-classifier.test.ts;CI=1 node scripts/run-vitest.mjs --reporter=basic extensions/codex/src/app-server/event-projector.test.ts extensions/codex/src/app-server/run-attempt.test.ts;git diff --check HEAD~1..HEAD;PROOF_HEAD=04469ace26ffdbdf8122a6b975b23251551addba TASK_WORKTREE=/e/worktree/openclaw-worktrees/issue-95474 pnpm exec tsx /e/worktree/openclaw-issue-95474-evidence/runtime-missing-tool-result-proof.mjs.runWithModelFallbackimplementation, configuredopenai/gpt-5.4plus fallbackanthropic/claude-opus-4-8, and observed onlyopenai/gpt-5.4,originalErrorRethrown: true, andfallbackAttempted: false.bashor missing native tool result should not silently switch a user's active session from the requested Codex model to a different provider, because the alternate provider cannot repair the local tool failure.isNonProviderRuntimeCoordinationError, the existing model failover boundary for local runtime conditions; this PR extends that boundary instead of adding downstream string checks in individual provider loops.src/agents/failover-error.test.tsandsrc/agents/model-fallback.test.ts, plus existing guard coverage insrc/agents/embedded-agent-runner/run/failover-policy.test.ts,src/agents/embedded-agent-runner/result-fallback-classifier.test.ts,extensions/codex/src/app-server/event-projector.test.ts, andextensions/codex/src/app-server/run-attempt.test.ts.missing_tool_resulterrors are non-provider/local;runWithModelFallbackdoes not make a second provider attempt; explicit provider HTTP metadata remains failover-eligible.missing_tool_resultand the stable Codex missing native tool-result sentence, and tests prove provider metadata still wins, so real provider failures continue to rotate/fail over as before.What Problem This Solves
When Codex synthesizes
missing_tool_resultfor a hung/missing local tool result, the failure is caused by local tool execution, not by the selected model provider. Before this change, the failover loop treated the resulting thrown error as an unknown candidate failure while fallback candidates remained, so a local shell/tool problem could silently switch a session from the requested model to the next configured provider.This PR keeps that failure on the current provider/model: the user sees the local tool failure instead of OpenClaw spending cross-provider fallback candidates that cannot fix the missing local tool result.
Root Cause
Codex can synthesize a failed tool result with
reason: "missing_tool_result"when a native tool call (for examplebash) never returns a matchingtool.result. That is a local tool-execution failure: switching fromopenai/gpt-5.4to a fallback provider cannot make the missing local shell result appear.The existing model failover guard already aborts failover for non-provider runtime coordination failures such as session write-lock contention and embedded session takeover. However, the
missing_tool_resultsentinel was not recognized by that guard, sorunWithModelFallbackreached the generic unknown-error branch and advanced to the next configured candidate.What Changed
missing_tool_resultnative Codex tool.call without a matching tool.resultrunWithModelFallback.Real behavior proof
missing_tool_resultlocal tool-execution failure no longer consumes model failover candidates or jumps to a different provider; it is rethrown on the current provider/model path.src/agents/model-fallback.tsfrom this worktree and configures a primary plus fallback provider.openai/gpt-5.4and fallbackanthropic/claude-opus-4-8, then threw the real Codex missing-tool-result-style error from the callback passed torunWithModelFallback. The observed JSON was:{ "proof": "missing_tool_result runtime failover boundary", "head": "04469ace26ffdbdf8122a6b975b23251551addba", "primary": "openai/gpt-5.4", "configuredFallback": "anthropic/claude-opus-4-8", "attempts": ["openai/gpt-5.4"], "originalErrorRethrown": true, "fallbackAttempted": false }This exits 0 and shows the local error stops after one candidate, rethrows the original error, and does not switch to the configured fallback provider. The focused failover regression tests and Codex app-server tests also exit 0 and cover the classification and sentinel production paths.
Review findings addressed
src/agents/model-fallback.ts, configures primaryopenai/gpt-5.4plus fallbackanthropic/claude-opus-4-8, throws the Codex missing-tool-result-style local error throughrunWithModelFallback, and exits 0 withattempts: ["openai/gpt-5.4"],originalErrorRethrown: true, andfallbackAttempted: false. This is redacted terminal/log-style output for the after-fix fallback boundary; I still did not run a live long-running Codex OAuth shell hang because that live environment is unavailable in this local PR repair context.missing_tool_resultis local tool execution, so surfacing the current-model local error is the expected behavior instead of consuming configured fallback providers that cannot repair the missing local tool result.missing_tool_resultand the stable native Codex missing tool-result sentence, and the tests keep explicit provider metadata such as HTTP 503 failover-eligible even when nested context contains the local sentinel.Verification
Run from the repository worktree after formatting and committing:
Regression Test Plan
src/agents/failover-error.test.tsandsrc/agents/model-fallback.test.tscover the new invariant;src/agents/embedded-agent-runner/run/failover-policy.test.ts,src/agents/embedded-agent-runner/result-fallback-classifier.test.ts,extensions/codex/src/app-server/event-projector.test.ts, andextensions/codex/src/app-server/run-attempt.test.tsguard adjacent policy and Codex behavior.{ result: { reason: "missing_tool_result" } }shapes are classified as local/non-provider, and a configured fallback chain does not make a second provider attempt for that local failure.Merge risk
missing_tool_resultand the stable Codex missing native tool-result sentence, and tests keep explicit HTTP/provider metadata authoritative.Not Changed
missing_tool_resultcreation path.Risk
Low/medium. The detection is intentionally narrow to Codex's stable missing-tool-result sentinel and only affects the already-existing non-provider abort path. The main regression risk would be accidentally suppressing a provider failure that happens to wrap a local sentinel, so the tests keep explicit provider metadata authoritative.