fix(claude-cli): return updatedInput in can_use_tool allow response for Claude Code 2.1#98665
Conversation
Claude Code >= 2.1.156 tightened its PermissionResult schema so an
approved can_use_tool control_response allow branch must carry an
updatedInput record. The Claude live-session bridge answered with the
deprecated { behavior: "allow" } shape and no updatedInput, so the CLI
rejected every approval-gated native tool call (Bash, WebFetch, and
AskUserQuestion among them) with a ZodError before the tool ran.
Echo the tool input back unchanged as updatedInput on the allow branch,
matching the shape the Claude Code 2.1 binary expects. The deny branch
and all other behavior are unchanged.
Fixes openclaw#95171.
|
Codex review: needs maintainer review before merge. Reviewed July 1, 2026, 10:51 AM ET / 14:51 UTC. Summary PR surface: Source +2, Tests +2. Total +4 across 2 files. Reproducibility: yes. source-level: current main and v2026.6.11 still emit the old allow response without 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. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land this narrow live-session response-shape fix as the canonical repair for the Claude Code 2.1 approval regression, while leaving the broader AskUserQuestion picker bridge to its separate issue/PR path. Do we have a high-confidence way to reproduce the issue? Yes, source-level: current main and v2026.6.11 still emit the old allow response without Is this the best way to solve the issue? Yes. Updating the Claude live stdio control-response boundary to echo the original tool input as AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 909be7bcbc70. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +2, Tests +2. Total +4 across 2 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
|
…penclaw#98665) Claude Code >= 2.1.156 tightened its PermissionResult schema so an approved can_use_tool control_response allow branch must carry an updatedInput record. The Claude live-session bridge answered with the deprecated { behavior: "allow" } shape and no updatedInput, so the CLI rejected every approval-gated native tool call (Bash, WebFetch, and AskUserQuestion among them) with a ZodError before the tool ran. Echo the tool input back unchanged as updatedInput on the allow branch, matching the shape the Claude Code 2.1 binary expects. The deny branch and all other behavior are unchanged. Fixes openclaw#95171.
…penclaw#98665) Claude Code >= 2.1.156 tightened its PermissionResult schema so an approved can_use_tool control_response allow branch must carry an updatedInput record. The Claude live-session bridge answered with the deprecated { behavior: "allow" } shape and no updatedInput, so the CLI rejected every approval-gated native tool call (Bash, WebFetch, and AskUserQuestion among them) with a ZodError before the tool ran. Echo the tool input back unchanged as updatedInput on the allow branch, matching the shape the Claude Code 2.1 binary expects. The deny branch and all other behavior are unchanged. Fixes openclaw#95171.
…penclaw#98665) Claude Code >= 2.1.156 tightened its PermissionResult schema so an approved can_use_tool control_response allow branch must carry an updatedInput record. The Claude live-session bridge answered with the deprecated { behavior: "allow" } shape and no updatedInput, so the CLI rejected every approval-gated native tool call (Bash, WebFetch, and AskUserQuestion among them) with a ZodError before the tool ran. Echo the tool input back unchanged as updatedInput on the allow branch, matching the shape the Claude Code 2.1 binary expects. The deny branch and all other behavior are unchanged. Fixes openclaw#95171.
What Problem This Solves
In a
claude-cli-backed session, every native tool call that Claude Code routes through its permission flow (control_request/can_use_tool) fails against@anthropic-ai/claude-code >= 2.1.156. OpenClaw answers an approved request with the deprecated{ behavior: "allow" }shape, but Claude Code 2.1 tightened itsPermissionResultschema so the allow branch now requires anupdatedInputrecord. The CLI rejects the response with aZodError(invalid_uniononupdatedInput) before the tool runs, so the turn stalls.This is the underlying failure behind issue #81099 (the
AskUserQuestionvariant that surfaces asinvalid_union on updatedInput) and is reported directly in issue #95171 (all approval-gated tools: Bash, WebFetch, Grep withpath, Glob outside cwd). Pinning Claude Code to2.0.77restores tool calls, confirming the break is on the OpenClaw bridge side.Why This Change Was Made
handleClaudeLiveControlRequestinsrc/agents/cli-runner/claude-live-session.tsbuilds thecontrol_response. On main the allow branch is:The allow object omits
updatedInput. Claude Code >= 2.1 validates the response against a union whose allow branch is{ behavior: "allow", updatedInput: <record>, ... }; a missingupdatedInputmatches neither branch, so validation throws before the tool executes.The fix echoes the tool input back unchanged as
updatedInput(OpenClaw does not modify the tool input, so the original input is the correct value):Boundary: this is the only Claude live-session surface that answers
can_use_tool. The otherbehavior: "allow"site,src/agents/harness/native-hook-relay.ts, is thecodex-only provider adapter (NATIVE_HOOK_RELAY_PROVIDERS = ["codex"]) emitting the settings-hookhookSpecificOutputschema, a different provider contract that Claude does not route through; it is intentionally untouched. The deny branch is unchanged.Dependency contract: the installed
claude2.1.197 binary emits the allow decision internally asbehavior:"allow",updatedInput:<input>and logsreturned updatedInput that failed schema validationon mismatch, matching theZodErrorin #95171.User Impact
claude-clisessions on Claude Code >= 2.1.156 can once again use native tools through the approval flow.AskUserQuestionand every other approval-gated tool stop failing with aZodErrorand the assistant turn proceeds. No config or migration is required; the deny path and existing behavior are unchanged.Evidence
Real behavior proof
Behavior addressed: on a
claude-clibackend, an approvedcan_use_toolrequest was answered with a shape Claude Code >= 2.1.156 rejects (invalid_uniononupdatedInput), stalling every native tool call includingAskUserQuestion.Real environment tested: drove the real prepared-run entry point
executePreparedCliRunwithbackend.liveSession = "claude-stdio"on pristineorigin/main(ce4a259) and on the patched tree, feeding a realcontrol_request/can_use_toolline for theAskUserQuestionpayload from issue #81099. The real live-session dispatcher, the realhandleClaudeLiveControlRequest, and the realwriteClaudeLiveControlResponsestay in play; only the spawned CLI OS process is stubbed, and the verbatim bytes OpenClaw writes to the CLI stdin are captured. The dependency contract is the installedclaude2.1.197 binary, whose allow shape isbehavior:"allow",updatedInput:<input>.Exact steps or command run after this patch: fed the
AskUserQuestioncan_use_toolrequest throughexecutePreparedCliRunand captured thecontrol_response.responseobject OpenClaw emits to the CLI.Evidence after fix:
Observed result after fix: before the change OpenClaw emits an allow decision with no updatedInput, the exact shape Claude Code 2.1 rejects; after the change it emits updatedInput carrying the unmodified tool input, matching the shape the 2.1.197 binary expects, so the approved tool proceeds.
What was not tested: no live network round-trip against Anthropic was run (that requires an authenticated model turn to emit a real tool call); full build not run.
Verification
node scripts/run-vitest.mjs src/agents/cli-runner.spawn.test.ts: the two allow control_response cases now assertupdatedInputround-trips the tool input; both pass with the patch and the added assertion fails on pristine main.oxfmt --checkandrun-oxlint.mjson the changed files: clean.run-tsgo.mjs -p tsconfig.core.jsonand the core-test tsconfig: clean.Related: #95171 (canonical report of this regression). Adjacent open PR #91544 targets the separate
AskUserQuestionpicker/answer bridge and does not change this line.