claude -p --output-format json returns empty result when final assistant message contains text + tool_use

Resolved 💬 2 comments Opened Apr 12, 2026 by racbart Closed May 23, 2026

Description

When using claude -p --output-format json, the result field in the JSON envelope is empty if the model's final assistant message contains both a text block and a tool_use block in the same response. The tool gets executed, the model has nothing more to say, and result is "".

The text IS stored in the session history and is visible when resuming with claude -r <session-id>. It's just not included in the envelope.

This is distinct from the v2.1.83 regression (#38805) where result was always empty — simple text-only responses work correctly on v2.1.104.

Version

  • Claude Code v2.1.104
  • Linux 6.8.0-107-generic (x86_64)

Steps to Reproduce

claude -p --output-format json --dangerously-skip-permissions --model claude-sonnet-4-6 \
  "Write a haiku about mountains, then call TodoWrite with a single completed todo. Output the haiku and tool call in one response. Do not write anything after the tool executes."

Reproduces consistently (3/3 runs tested).

Expected Behavior

result should contain the text content from the assistant's response.

Actual Behavior

{
  "result": "",
  "stop_reason": "end_turn",
  "output_tokens": 291,
  ...
}
  • result is "" despite tokens being generated
  • Resuming the session interactively shows the text was produced

Root Cause

The model produces a single message with two content blocks:

  1. text block (the actual output)
  2. tool_use block (e.g. TodoWrite)

The CLI executes the tool, receives the tool_result, and the model stops (no further text). The envelope's result field appears to be populated only from a final text-only assistant turn — which doesn't exist in this case.

Impact

Any claude -p caller that relies on the result field will silently receive an empty string when the model happens to end its response with a tool call alongside text output. This is especially likely with agents that use TodoWrite for task tracking.

Workaround

Resume the same session with a follow-up prompt. A naive "repeat your last response" does not work — the agent often doesn't realize it produced text before the tool call, and the recovery itself can trigger the same bug if the agent uses tools again.

What works reliably (tested 5/5 across Opus and Sonnet):

claude -p --output-format json --dangerously-skip-permissions \
  --resume <session-id> \
  "The text you wrote in your previous message was lost in transit. I can see you wrote something before the tool call, but I cannot read it. Please re-send that exact text, prefixed with RE-SENDING: on the first line. Do not call any tools, just output text."

Key elements:

  • Tell the agent its text was "lost in transit" and that you can see it wrote something before the tool call — without this context the agent often responds with "No response requested"
  • Ask for a RE-SENDING: prefix to anchor a text-only response (strip it from the result afterward)
  • Explicitly say "do not call any tools" to prevent the same bug from recurring

---

🤖 Generated by Claude Code

View original on GitHub ↗

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