claude CLI 2.1.215 emits tool_use with empty input in stream-json (tools never execute)

Status Open
Reported on v2.1.215
Maintainer reply None cached
Activity 2 comments · opened Aug 4, 2026

Bug Report: claude CLI 2.1.215 emits tool_use with empty input in stream-json (tools never execute)

Reported on behalf of a cc-suite / claude-octopus user. Root cause traced to the @anthropic-ai/claude-code CLI binary.

Environment

  • OS: macOS (darwin)
  • claude CLI: 2.1.215 (@anthropic-ai/claude-code)
  • Node: v22.23.1 (nvm)
  • Also affects: @anthropic-ai/claude-agent-sdk@0.2.141 (bundled by claude-octopus@1.2.0), since it drives the CLI via stream-json.

Summary

When the CLI is driven via --output-format stream-json, every tool_use block is emitted with "input": {}. The tool then fails validation (tool_result.is_error = true, "required parameter file_path is missing"), the agent loops, and the run ends at error_max_turns without performing any action. The same failure occurs in normal (non-stream-json) mode, so the empty input is produced before output serialization — not a stream-json-only artifact.

Steps to reproduce (self-contained, no SDK needed)

claude -p "Use the Write tool to create /tmp/repro_foo.txt containing exactly the word HI." \
  --output-format stream-json --verbose --permission-mode acceptEdits --max-turns 3

Expected

  • tool_use.input carries the parameters the model produced (e.g. { "file_path": "/tmp/repro_foo.txt", "content": "HI" }).
  • The Write tool executes and the file is created.

Actual

  • tool_use.input is {} for every tool call.
  • tool_result reports is_error: true with InputValidationError (missing file_path / content); no filesystem action occurs; run ends at error_max_turns.

Raw stream-json evidence

Command:

claude --output-format stream-json --verbose --input-format stream-json \
  --permission-mode acceptEdits --max-turns 3
# stdin (stream-json):
# {"type":"user","message":{"role":"user","content":"Use the Write tool to create /tmp/repro_foo.txt containing exactly the word HI."}}

Captured verbatim lines (loop repeats 3× with identical empty input):

{"type":"assistant","message":{"id":"msg_a783accd83ef5fc899917024","type":"message","role":"assistant","model":"claude-opus-4-8","content":[{"type":"tool_use","id":"call_00_LqNuN6TflHZEAHXBISGx4615","name":"Write","input":{}}],"stop_sequence":null,"context_management":null},"parent_tool_use_id":null,"session_id":"17a4ff84-ff6e-495d-82bc-757fa045ebd6","uuid":"af1efe2e-0c07-47c3-b873-0c8f894f32c3","timestamp":"2026-08-04T07:53:51.394Z"}

{"type":"user","message":{"role":"user","content":[{"type":"tool_result","content":"<tool_use_error>InputValidationError: Write failed due to the following issues:\nThe required parameter `file_path` is missing\nThe required parameter `content` is missing</tool_use_error>","is_error":true,"tool_use_id":"call_00_LqNuN6TflHZEAHXBISGx4615"}]},"parent_tool_use_id":null,"session_id":"17a4ff84-ff6e-495d-82bc-757fa045ebd6","uuid":"2c706d20-a530-4019-848d-d9cf5faac172","timestamp":"2026-08-04T07:53:51.398Z","tool_use_result":"InputValidationError: [\n  {\"expected\":\"string\",\"code\":\"invalid_type\",\"path\":[\"file_path\"],\"message\":\"Invalid input: expected string, received undefined\"},\n  {\"expected\":\"string\",\"code\":\"invalid_type\",\"path\":[\"content\"],\"message\":\"Invalid input: expected string, received undefined\"}\n]"}

{"type":"result","subtype":"error_max_turns","duration_ms":10902,"is_error":true,"num_turns":4,"stop_reason":"tool_use","session_id":"17a4ff84-ff6e-495d-82bc-757fa045ebd6","total_cost_usd":0.004875,"errors":["Reached maximum number of turns (3)"]}

Note the tool_result proves the parameters are genuinely absent (received undefined), not merely omitted from display.

Suspected location (from inspecting the shipped 2.1.215 binary — to be confirmed by maintainers)

The CLI builds tool_use.input by accumulating the model stream's input_json_delta partial JSON, in a handler roughly of the form:

case "input_json_delta":
  if (n && Ial(n)) {            // Ial = tool_use type guard
    let o = n[wal] || "";       // wal = hidden field holding accumulated partial_json
    o += t.delta.partial_json;
    let i = { ...n };
    if (o) try { i.input = parse(o) } catch {}
    r.content[t.index] = i;
  }

The stream-json output formatter then relays message.content[].tool_use.input verbatim (e.g. yield { type:"assistant", message:n.message, ... }), so it does not strip input. The emptiness therefore originates in the accumulation step — likely the Ial(n) guard / hidden wal field not matching this build's internal tool_use block shape, so partial_json never lands in input.

Impact

Any caller driving the CLI through stream-json (including the official @anthropic-ai/claude-agent-sdk, and thus claude-octopus / cc-suite's claude_code MCP tool) receives empty tool inputs and cannot execute tools.

Notes

  • Not an auth/permission issue: --permission-mode acceptEdits is set; the failure is missing parameters, not a denial.
  • Model observed: claude-opus-4-8.
  • Happy to provide the minimal @anthropic-ai/claude-agent-sdk query() repro or additional captures if useful.

Minimal @anthropic-ai/claude-agent-sdk repro (alternative entry point)

import { query } from "@anthropic-ai/claude-agent-sdk";
const q = query({
  prompt: 'Use the Write tool to create /tmp/repro.txt with content HI.',
  options: { cwd: "/tmp", permissionMode: "acceptEdits", maxTurns: 4 },
});
for await (const m of q) {
  if (m.type === "tool_use") console.log("name=", m.name, "input=", JSON.stringify(m.input));
  // -> name= Write input= {}   (repeated, then error_max_turns)
}

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗