[BUG] tool calls fail when pipe is used

Resolved 💬 3 comments Opened Feb 23, 2026 by gclayburg Closed Feb 27, 2026

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_

View original on GitHub ↗

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