`--output-format text` silently drops all output when session ends on a tool call (exit 0, no error)
Description
When using claude -p (non-interactive/programmatic mode) with --output-format text, if the model's final turn is a tool call rather than a text response, stdout is completely empty. The process exits 0 with no error, no warning, nothing. You get silence where you expect content.
This is not a rare edge case. In a production cron job running parallel analysis workers, this caused a 58% analysis failure rate before we identified the cause. The model interleaves text with tool calls frequently — especially when given access to MCP servers or any tools. On those sessions, the text output is silently discarded.
The fix works and is simple (--verbose flag, see Workaround below), but there's no documentation anywhere indicating this behavior exists or what to do about it. It took production failures and debugging to find it.
Steps to Reproduce
# Simple reproduction — use a prompt that will invoke a tool call
claude -p \
--output-format text \
--max-turns 3 \
"Read the file /tmp/test.txt and summarize it"
If the model's final turn happens to be a tool call (e.g., the Read tool invocation) rather than a text response, stdout will be empty despite the session completing successfully.
More reliably reproducible with a prompt that encourages tool use at the end of the session:
# Create a test file first
echo "Hello world" > /tmp/test.txt
# This will silently drop output if the session ends on a tool call
claude -p \
--output-format text \
"Use the Read tool to read /tmp/test.txt. After reading it, use the Bash tool to list the files in /tmp."
Expected output: the model's text responses during the session.
Actual output: empty string (stdout), exit code 0.
The failure is non-deterministic depending on whether the LLM's final turn happens to be a tool call or a text response. This makes it especially hard to catch in testing — the same prompt can succeed or fail depending on how the model structures its response.
Expected Behavior
--output-format text should return all text output generated during the session, regardless of what the final turn's type is. If the session ends on a tool call, the preceding text output should still be captured and returned.
Or, at minimum: if output will be dropped, the process should exit non-zero or write something to stderr so the caller knows the output is empty for a reason, not because the model produced nothing.
Actual Behavior
- stdout is completely empty
- exit code is 0
- no stderr output
- no indication anything went wrong
From the caller's perspective, the model produced no output. There is no way to distinguish "model produced no text" from "output was silently dropped."
Workaround
Add --verbose to the command. With --verbose, all output is captured reliably regardless of whether the session ends on a tool call.
# With --verbose: output is captured reliably
claude -p \
--output-format text \
--verbose \
"Read /tmp/test.txt and summarize it"
Note: --verbose produces additional metadata in the output stream. If you need clean text output with no tool call interference at all, disable tools entirely:
# Cleanest option for pure text generation (no tools, no interleaving):
claude -p \
--output-format text \
--verbose \
--tools "" \
--strict-mcp-config \
"Your prompt here"
The --verbose flag appears to be the switch that controls whether text output is buffered vs. streamed through. Without it, text that was generated before the final tool call is discarded when --output-format text is used.
Impact
This is a significant footgun for anyone building production automation with claude -p:
- Failure is silent (exit 0, no error)
- Failure rate depends on LLM behavior, which is non-deterministic
- In my production environment (parallel retro analysis workers), this produced a 58% failure rate before identification
- The fix is one flag, but there's nothing in the docs or error output pointing toward it
Anyone building batch processing, cron jobs, or any headless automation with claude -p and tools enabled is vulnerable to this.
Environment
Claude Code version: 2.1.80
OS: macOS (Darwin 25.3.0)
Shell: /bin/bash
MCP servers enabled: yes (multiple — reddit-mcp-buddy, claude-in-chrome, Slack)
Additional Notes
Related behavior: --output-format stream-json already requires --verbose (the CLI exits with an explicit error if you try to use them without it). The text format has the same underlying dependency on --verbose for reliable output, but fails silently instead of erroring out. Making the text format fail the same way stream-json does (exit non-zero with a clear error) would at least make this discoverable.
The fix was also applied to multiple scripts in our codebase after discovery — content-expand.sh, news-feed-monitor.sh, and the retro analysis runner. All had the same silent failure mode.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗