Skip to content

fix(agents): prevent Codex missing_tool_result from triggering cross-provider model fallback#95520

Closed
zhangqueping wants to merge 2 commits into
openclaw:mainfrom
zhangqueping:fix/issue-95474
Closed

fix(agents): prevent Codex missing_tool_result from triggering cross-provider model fallback#95520
zhangqueping wants to merge 2 commits into
openclaw:mainfrom
zhangqueping:fix/issue-95474

Conversation

@zhangqueping

@zhangqueping zhangqueping commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Summary

When the Codex harness synthesizes a missing_tool_result for a hung native bash call, the error percolates through the failover classification pipeline without matching any known provider-failure pattern. The outer runWithModelFallback loop in model-fallback.ts treats unrecognized errors as retryable when remaining model candidates exist (line 1789), causing a cross-provider model fallback that silently switches the user's model mid-conversation. No other provider/model can fix a local tool execution failure.

This fix adds detection of the Codex MISSING_TOOL_RESULT_ERROR text pattern in isNonProviderRuntimeCoordinationError, which is already checked at both the inner runner (line 366) and outer fallback loop (line 1717) in model-fallback.ts. When detected, the error is treated as a non-provider local failure and the fallback chain aborts immediately instead of consuming candidate slots.

Fixes #95474

Real behavior proof (required for external PRs)

Behavior addressed: Codex missing_tool_result (synthetic error for hung native tool calls) triggers cross-provider model fallback, silently switching the user's model mid-conversation to a different provider that also cannot fix the local tool failure.

Real setup tested:

  • Runtime: Node 22.19, Linux 4.19.112-2.el8.x86_64
  • Code analysis path: See root cause analysis below
  • Test framework: Vitest 4.1.8

Root cause: The outer fallback loop in model-fallback.ts:1789 assumes all unrecognized (non-FailoverError) errors are retryable when remaining candidates exist. The Codex MISSING_TOOL_RESULT_ERROR ("OpenClaw recorded a native Codex tool.call without a matching tool.result before the turn completed.") does not match any failover classification pattern, so coerceToFailoverError returns null. The raw error becomes normalized at line 1737, isKnownFailover is false at line 1792, and the loop continues to the next model candidate — even though no other provider can fix a local command.

Exact steps or command run after this patch:

cd /home/0668001085/workspace/openclaw-worktrees/fix/issue-95474
node_modules/.bin/vitest run src/agents/failover-error.test.ts --config vitest.config.ts --reporter=verbose
node_modules/.bin/vitest run src/agents/model-fallback.test.ts --config vitest.config.ts --reporter=verbose
pnpm format:check src/agents/failover-error.ts src/agents/failover-error.test.ts

After-fix evidence:

✓ |unit-fast| failover-error > isNonProviderRuntimeCoordinationError > returns true for direct Codex missing native tool result errors
✓ |unit-fast| failover-error > isNonProviderRuntimeCoordinationError > returns true when the missing tool result error is nested via cause
✓ |unit-fast| failover-error > isNonProviderRuntimeCoordinationError > returns true when the missing tool result error is nested via error property
✓ |unit-fast| failover-error > isNonProviderRuntimeCoordinationError > returns false for unrelated error messages that mention 'tool'

 Test Files  1 passed (1)
      Tests  95 passed (95)

 Test Files  2 passed (2)
      Tests  164 passed (164)  (model-fallback regression suite)

$ pnpm format:check src/agents/failover-error.ts src/agents/failover-error.test.ts
Checking formatting...
All matched files use the correct format.
Finished in 18ms on 2 files using 1 threads.

Observed result after the fix: isNonProviderRuntimeCoordinationError now returns true for errors containing the Codex MISSING_TOOL_RESULT_ERROR pattern (both direct and nested via cause/error properties). The model fallback chain will abort on these local tool execution failures instead of advancing to the next candidate. All existing tests continue to pass with no regressions.

What was not tested: Live end-to-end reproduction with a hung Codex bash call. The fix is validated at the source level with unit tests covering the exact error message shape the Codex harness produces during synthesizeMissingToolResults (confirmed by ClawSweeper source-repro review).

Tests and validation

Test Type Result Details
Unit Tests 95/95 passed (4 new + 91 existing) in failover-error.test.ts
Regression (model-fallback) 164/164 passed
Format (oxfmt) Clean on both changed files

Risk checklist

Did user-visible behavior change? Yes - Users encountering hung Codex native tool calls will no longer see their model silently switch to a different provider. The error will surface as a terminal error on the same model, which is the intended behavior.

Did config, environment, or migration behavior change? No

Did security, auth, secrets, network, or tool execution behavior change? No

Current review state

What is the next action? Maintainer review requested

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: S labels Jun 21, 2026
@clawsweeper

clawsweeper Bot commented Jun 21, 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 as superseded: this PR targets the same missing-tool-result fallback bug, but #95543 is open, mergeable, proof-positive, and covers the same central guard with stronger runtime-boundary proof plus provider-metadata precedence coverage.

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 details

Best 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 consumes remaining candidates for unclassified errors. 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 that this PR lacks.

Security review:

Security review cleared: The diff changes agent error classification 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:

  • Vincent Koc: Current blame for the non-provider coordination guard, outer fallback loop, and Codex missing-result synthesis points to recent main work in these paths. (role: recent area contributor; confidence: high; commits: 7dd01d15c56d, 12c34fc3a951; files: src/agents/failover-error.ts, src/agents/model-fallback.ts, extensions/codex/src/app-server/event-projector.ts)
  • Sid: git log -S shows the unrecognized-error fallback continuation behavior was introduced in the model fallback loop by commit 156f13aa64cd. (role: introduced fallback-loop behavior; confidence: medium; commits: 156f13aa64cd; files: src/agents/model-fallback.ts)

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

@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: 🚨 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
…provider model fallback

- Add hasLocalNativeToolExecutionFailure to detect synthetic missing tool
  result errors from the Codex harness in failover-error.ts
- Extend isNonProviderRuntimeCoordinationError to abort model fallback
  for local native tool execution failures (no other model can fix them)
- Add unit tests for direct, nested (cause), and nested (error property)
  missing tool result detection

Fixes openclaw#95474
@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. 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 21, 2026
@clawsweeper

clawsweeper Bot commented Jun 21, 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: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P2 Normal backlog priority with limited blast radius. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. size: S 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]: missing_tool_result (local tool-execution failure) is classified unclassified and triggers cross-provider model fallback

1 participant