[BUG] Bash tool hangs indefinitely when 2>&1 combined with pipe — regression in 2.1.x, high frequency with non-Anthropic backends
Describe the bug
Bash tool commands that combine 2>&1 (stderr→stdout redirect) with a pipe (|) cause the CLI to hang indefinitely — no output, no timeout, no recovery. Requires killing the Claude Code process.
This is a regression in 2.1.x. Did not occur on older versions.
Related closed issues (same root cause, never fixed)
- #19060 — "CLI freezes with No messages returned" — same
2>&1 | teetrigger, closed as "not planned" without fix - #5264 — "Bash tool treats 2>&1 as literal arguments" — closed as duplicate, root cause never addressed
Environment
- Claude Code version: 2.1.150
- Model backend: DeepSeek V4 Pro (via
ANTHROPIC_BASE_URL) - OS: Windows 11 Home (10.0.26100)
- Shell: bash (Git Bash / MSYS2)
Steps to reproduce
- Make any Bash tool call that uses
2>&1combined with| - Wait — the command hangs indefinitely
Examples that trigger the hang:
mvn compile 2>&1 | grep ERROR
npm run build 2>&1 | tail -20
find . -name "*.java" 2>&1 | wc -l
Frequency
~3 times per 10 minutes during normal development workflow.
Expected behavior
Command executes and returns output normally, like in any standard shell.
Workaround (confirmed)
Three alternatives work:
- Drop
2>&1(preferred) — Bash tool already captures and merges both stdout and stderr:
``bash``
mvn compile | grep ERROR
- Wrap in
bash -c:
``bash``
bash -c 'mvn compile 2>&1 | grep ERROR'
- Two-step with file:
``bash``
mvn compile > /tmp/build.log 2>&1
grep ERROR /tmp/build.log
Root cause speculation
The Bash tool's command parser was significantly refactored in the 2.1.145–2.1.149 range. The bash -c workaround working while the bare command hangs confirms the parser is mishandling shell metacharacters — likely tokenizing 2>&1 as literal positional arguments instead of a shell redirect operator, which then interacts poorly with the pipe splitting logic.
With non-Anthropic backends (DeepSeek, OpenRouter, etc.), the frequency is much higher — possibly because the stream/timing characteristics differ, exposing race conditions in the output capture mechanism.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗