[BUG] tool calls fail when pipe is used
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
When the Claude Code Bash tool executes a command containing a pipe (|), the last command in the pipeline has its stdin redirected from /dev/null instead of from the pipe. The result is that piped commands silently produce no output and exit with code 1.
The impact of this bug is that many tool calls fail. This is especially true when claude code is trying to consume a rest api and process it with a pipe to jq. Claude code tries to fix things by doing different tool calls. Sometimes they fail too. Eventually it kind of figures out something that kind of works, but in the mean time there were many wasted tool calls. Claude code itself has no visibility into why the tools it tries to run are failing. Claude code could be so much smarter about execting tools like this.
Environment
- Claude Code (claude-sonnet-4-6)
- v2.1.50
- macOS Darwin 24.6.0, bash shell
- Reproduced consistently across multiple test cases
How to Reproduce
Ask Claude Code to run any command containing a pipe where the last command reads from stdin:
echo $HOME | grep User
Expected output: /Users/gclaybur
Actual output: (empty), exit code 1
Adding a sleep prefix allows you to observe the process with ps:
sleep 61 && echo $HOME | grep User
While sleep is running, capture:
ps auxww | grep sleep
Observed Behavior
The ps output reveals exactly what Claude Code executes:
Non-piped command (works correctly):
/bin/bash -c ... eval 'sleep 62 && echo $HOME' \< /dev/null && pwd -P >| /tmp/...
Piped command (broken):
/bin/bash -c ... eval 'sleep 61 && echo $HOME | grep User' \< /dev/null && pwd -P >| /tmp/...
The structure is identical — \< /dev/null always appears as tokens after the closing single quote, i.e., as additional arguments to eval.
Root Cause Analysis
\< escapes < from being treated as a shell redirect operator, making it a literal word argument. eval receives three arguments and joins them with spaces before evaluating:
| Argument | Value |
|----------|-------|
| 1 | sleep 61 && echo $HOME \| grep User |
| 2 | < (from \<) |
| 3 | /dev/null |
The string eval actually evaluates:
sleep 61 && echo $HOME | grep User < /dev/null
Shell operator precedence makes < bind to the nearest preceding command:
sleep 61 && echo $HOME | (grep User < /dev/null)
grep's stdin is redirected from /dev/null (an empty file) instead of from the pipe. echo $HOME writes to a pipe that nobody is reading. grep finds nothing, exits 1.
Why Non-Piped Commands Are Unaffected
For eval 'sleep 62 && echo $HOME' < /dev/null, the < /dev/null appends to echo $HOME, redirecting echo's stdin. Since echo never reads stdin, this redirect is harmless and the command works correctly.
The bug only surfaces when the last command in the pipeline reads from stdin (e.g., grep, jq, wc, head, sed, awk, etc.).
Expected Behavior
Piped commands passed to the Bash tool should behave identically to the same commands run directly in a terminal. echo $HOME | grep User should output /Users/gclaybur.
Intended Purpose vs. Implementation
The \< /dev/null is presumably intentional — preventing commands from accidentally blocking on terminal input. However, it should be applied as a redirect on the outer bash -c process, not as arguments to eval:
# Current (broken for pipes):
/bin/bash -c "... && eval 'cmd' \< /dev/null && ..."
# Correct:
/bin/bash -c "... && eval 'cmd' && ..." < /dev/null
What Should Happen?
see What's Wrong section
Error Messages/Logs
Steps to Reproduce
See What's Wrong section
Claude Model
Sonnet (default)
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.50 (Claude Code)
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Cursor
Additional Information
_No response_
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗