openclaw - 💡(How to fix) Fix [Bug]: Claude CLI live-session bridge ignores `control_request` (`can_use_tool`) — sessions stall until 180/600s timeout — reproduces on `2026.5.3-1`

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Error Message

From the user's perspective, the model answered the first turn, then made one tool call, and the session simply stopped — no error in the UI until the timeout fires. The bridge should consume type: "control_request" messages from Claude CLI's stream-json output and write a matching type: "control_response" to stdin so Claude CLI can either run the native tool or fail fast with a meaningful error.

  • For other subtypes, write a control_response error explaining the bridge does not yet support that subtype.
  • Logs the decision (tool name, decision, policy), and treats unsupported subtypes as a structured error response rather than a no-op.

Root Cause

When the Claude CLI live-session backend (src/agents/cli-runner/claude-live-session.ts, bundled as dist/claude-live-session-*.js) is used as the claude-cli provider, the bridge does not respond to Claude CLI control_request messages of subtype can_use_tool. Claude CLI emits these control_request events when it wants to use one of its native tools (e.g. native Bash, Read, Edit) and is waiting for a permission decision. Because OpenClaw's bridge silently ignores them, the CLI sits forever waiting for a control_response, and the live session eventually times out:

Fix Action

Fix / Workaround

Workaround / patch we are running locally

We patched the live bundle to:

The patch wires the resolved policy through params.controlRequestPolicy in the helpers that hash params/build sessions, so the policy is captured at session-start time rather than recomputed mid-stream.

Code Example

queued_work_without_active_run
classification=stale_session_state
recovery skipped: active_reply_work
... CLI produced no output for {180,600}s and was terminated.
RAW_BUFFERClick to expand / collapse

What happened?

When the Claude CLI live-session backend (src/agents/cli-runner/claude-live-session.ts, bundled as dist/claude-live-session-*.js) is used as the claude-cli provider, the bridge does not respond to Claude CLI control_request messages of subtype can_use_tool. Claude CLI emits these control_request events when it wants to use one of its native tools (e.g. native Bash, Read, Edit) and is waiting for a permission decision. Because OpenClaw's bridge silently ignores them, the CLI sits forever waiting for a control_response, and the live session eventually times out:

queued_work_without_active_run
classification=stale_session_state
recovery skipped: active_reply_work
... CLI produced no output for {180,600}s and was terminated.

From the user's perspective, the model answered the first turn, then made one tool call, and the session simply stopped — no error in the UI until the timeout fires.

What did you expect to happen?

The bridge should consume type: "control_request" messages from Claude CLI's stream-json output and write a matching type: "control_response" to stdin so Claude CLI can either run the native tool or fail fast with a meaningful error.

It should also map decisions to OpenClaw's effective exec policy for the agent:

  • If the resolved exec policy is security: full, ask: off, allow native Bash (matches the existing --dangerously-skip-permissions semantics OpenClaw uses elsewhere when the policy is fully permissive).
  • Otherwise, deny native Bash cleanly with a message that points the agent at OpenClaw MCP tools instead.
  • For non-Bash native tools (Read, Edit, Write, etc.), deny with a message that says "use OpenClaw MCP tools instead", since OpenClaw's policy/audit pipeline expects the MCP path.

Reproduction steps

  1. Configure a Claude CLI backend in agents.defaults.cliBackends.claude-cli so it runs the bundled binary with stream-json (-p, --output-format stream-json, --input-format stream-json, --verbose, --setting-sources user, --allowedTools mcp__openclaw__*, --permission-mode default).
  2. Pick any agent and switch its model to claude-cli/claude-opus-4-6 (or 4-7) in the OpenClaw Control UI.
  3. In a fresh dashboard session, ask the model to do something that triggers a Claude native tool — e.g., "tail the last 50 lines of /var/log/syslog" (Claude will try native Bash).
  4. Observe: the model runs one tool call, then nothing further. After 180s (or 600s for resumed turns) you'll see the stuck-detector + termination log lines above. Earlier turns may succeed if they happened to use only OpenClaw MCP tools.

Reproduces on

  • openclaw 2026.5.3-1 (Docker container, Linux 6.12.24-Unraid x64, node v24.14.0)
  • dist/claude-live-session-D20mQ3LE.js (bundle hash visible in install) — source: src/agents/cli-runner/claude-live-session.ts
  • Claude Agent SDK CLI bundled at /app/node_modules/@anthropic-ai/claude-agent-sdk-linux-x64/claude
  • Claude models: claude-opus-4-6, claude-opus-4-7

Workaround / patch we are running locally

We patched the live bundle to:

  1. Resolve and persist a controlRequestPolicy per session derived from agent-level tools.exec (falling back to global tools.exec), with { allowNativeBash, ask, security }.
  2. On stream-json parsed.type === "control_request":
    • For subtype: "can_use_tool":
      • If tool_name === "Bash" and controlRequestPolicy.allowNativeBash, write a control_response of { behavior: "allow", updatedInput }.
      • Otherwise write { behavior: "deny", message, decisionClassification: "user_reject" } with a message that explains the policy and points at OpenClaw MCP tools.
    • For other subtypes, write a control_response error explaining the bridge does not yet support that subtype.
  3. Silently ignore parsed.type === "control_response" (we never originate requests).

The patch wires the resolved policy through params.controlRequestPolicy in the helpers that hash params/build sessions, so the policy is captured at session-start time rather than recomputed mid-stream.

A diff of the patched bundled JS (against the unpatched bundle in the same install) is small (~80 lines, mostly the new helpers and the dispatch lines).

Suggested upstream fix

A real source-tree fix in src/agents/cli-runner/claude-live-session.ts that:

  • Resolves an effective exec policy per session/agent at start time and threads it into the session record.
  • Adds a control_request handler that maps can_use_tool decisions through that policy and emits control_response to stdin.
  • Logs the decision (tool name, decision, policy), and treats unsupported subtypes as a structured error response rather than a no-op.

Without this, any Claude CLI-backed session that triggers a native tool prompt will appear to hang and only fail at the global no-output timeout, which is a confusing UX for OpenClaw operators.

Anything else?

Happy to open a draft PR against src/agents/cli-runner/claude-live-session.ts mirroring the local patch — let me know if maintainers prefer that, and whether there's a preferred shape for the policy-resolution helper (some helpers.ts patterns exist in the same directory).

Reported for openclaw 2026.5.3-1.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - 💡(How to fix) Fix [Bug]: Claude CLI live-session bridge ignores `control_request` (`can_use_tool`) — sessions stall until 180/600s timeout — reproduces on `2026.5.3-1`