[BUG] Bash tool hides errors and hangs from non-terminating commands when output is piped through `tail`/`head` (or any EOF-buffering filter)
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Summary
The Bash tool's output capture pattern silently drops stdout/stderr from any command whose downstream filter (tail -N, head -N, sort, etc.) emits only on EOF. When the upstream command hangs, crashes mid-stream without closing its pipe, or simply runs longer than the agent expects, the agent sees an empty result — indistinguishable from a quiet success. Errors that the user could see directly in a terminal become invisible to the model.
Reproduction
Any Claude Code session where the agent reaches for the idiomatic pattern:
RAILS_ENV=test bin/rails test 2>&1 | tail -40
npm run build 2>&1 | tail -50
pytest -q 2>&1 | tail -30
If the upstream process:
- writes output and then hangs (deadlock, waiting on stdin, server starts and never exits),
- crashes with an error that prints to stderr but doesn't close all open file descriptors promptly, or
- is killed by the harness timeout before it terminates cleanly,
then tail -N is still blocked reading its input, has emitted nothing, and the Bash tool's response is empty. The agent then has no signal to act on — it concludes "no output, must be OK" and proceeds, or it concludes "I should try again" and re-runs the same broken command.
We hit this concretely in a Rails project: RAILS_ENV=test bin/rails test 2>&1 | tail -40 hid a Minitest.run_one_method failure for several minutes. The user had to manually intervene and tell the agent the test suite was broken — there was no way for the agent to discover it on its own.
Why it happens
tail -N (without -f) is fundamentally an EOF-driven filter. It must consume the entire stream before it knows which N lines are last. The pipe doesn't close until the upstream process terminates. So for any non-terminating upstream, tail is a black hole. The agent's 2>&1 | tail -N habit is a coping mechanism for context-window limits, but the cost is silently dropping the very output the agent needs to debug failures.
The model's own training reinforces the pattern (it's the standard Unix-y way to "show me the last bit of a long output"), so it will keep reaching for it unless the harness itself makes the pattern safe — or makes a better pattern equally cheap.
Impact
This affects every Claude Code user who runs builds, tests, servers, migrations, or any potentially-hanging command. The current model habit of cmd 2>&1 | tail -N is exactly the wrong tool for the cases where the user most needs the agent to detect and report failures. Users have to babysit the agent and surface the error manually — which is the failure mode this tool was built to prevent.
What Should Happen?
Proposed fixes
In rough order of effort vs. payoff:
- Always tee to a durable file. Have the Bash tool capture full stdout/stderr to
$CLAUDE_JOB_DIR/<id>.log(or equivalent) and present both the tail-truncated text and a path the agent canReadfor the rest. Then even if the agent reaches for| tail -N, the raw output exists and is inspectable. This is the smallest change with the biggest payoff.
- Stream output incrementally rather than presenting a single EOF-delimited block. A PTY-style capture or periodic flushing of partial stdout into the tool result would let the agent see "the command produced 300 lines and then went quiet" vs. "the command produced 0 lines."
- Update the agent's system prompt / tool guidance to actively discourage
| tail -Nin the Bash tool's docstring, and to recommendrun_in_background/Monitor/ "tee to file thenRead" as defaults. The current Bash docstring warns againstcat/sed/echobut not against the much more dangerous| tail -Npattern.
- Add a
max_output_linesparameter to the Bash tool that does truncation server-side, after the command terminates, instead of forcing the agent to build a fragile pipeline. The harness knows when to truncate without losing the underlying stream.
Any of (1)–(4) would be a material improvement; (1) is the smallest, most defensible change.
Error Messages/Logs
Steps to Reproduce
Reproduction
Any Claude Code session where the agent reaches for the idiomatic pattern:
RAILS_ENV=test bin/rails test 2>&1 | tail -40
npm run build 2>&1 | tail -50
pytest -q 2>&1 | tail -30
If the upstream process:
- writes output and then hangs (deadlock, waiting on stdin, server starts and never exits),
- crashes with an error that prints to stderr but doesn't close all open file descriptors promptly, or
- is killed by the harness timeout before it terminates cleanly,
then tail -N is still blocked reading its input, has emitted nothing, and the Bash tool's response is empty. The agent then has no signal to act on — it concludes "no output, must be OK" and proceeds, or it concludes "I should try again" and re-runs the same broken command.
Claude Model
Opus
Is this a regression?
No, this never worked
Last Working Version
_No response_
Claude Code Version
2.1.150 (Claude Code)
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
_No response_
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗