[BUG] Pipe operator (|) in quoted strings incorrectly interpreted as shell pipe in Bash tool
Environment
- Platform (select one):
- [x] Anthropic API
- [ ] AWS Bedrock
- [ ] Google Vertex AI
- [ ] Other:
- Claude CLI version: v1.0.43
- Operating System: Linux (detected as Ubuntu-based from environment)
- Terminal: vs code
Bug Description
Pipe operator (|) in quoted strings is incorrectly interpreted as shell pipe operation in Bash tool calls,
causing commands to fail with generic "Error" instead of treating the pipe as a literal character within quotes.
Steps to Reproduce
- Open Claude Code and use the Bash tool
- Execute any command with
|inside quoted strings, for example:-
rg -n "^import|^from" /path/to/file.py -
grep -E "pattern1|pattern2" file.txt -
sed "s/old1|old2/new/" file.txt
-
- Observe the command fails with generic "Error" message
Expected Behavior
Pipe operator inside quoted strings should be treated as literal character (regex alternation, etc.), not as shell pipe operator. The same commands work correctly when executed directly in shell.
Actual Behavior
- Commands fail with generic "Error" output
- Using
--tracewith ripgrep reveals corrupted regex parsing with shell redirection artifacts (e.g.,< /dev/nullappears in parsed regex) - No meaningful error message is provided
Additional Context
This affects all commands expecting literal pipe characters in quoted arguments, including regex patterns with alternation. Debug trace shows the issue is in quote parsing/shell interpretation within the Bash tool environment. Workarounds include splitting into multiple commands or using internal tools (Grep, Glob, etc.) instead of Bash+external tools.
Example from chat
Bash(rg --trace -n "^import|^from" /home/michael/work/wdt/mp2/mp2c/md/py/cli.py)
⎿ Error: rg: DEBUG|rg::flags::parse|crates/core/flags/parse.rs:97: no extra arguments found from configuration file
rg: DEBUG|rg::flags::hiargs|crates/core/flags/hiargs.rs:1083: number of paths given to search: 1
rg: DEBUG|rg::flags::hiargs|crates/core/flags/hiargs.rs:1094: is_one_file? true
rg: DEBUG|rg::flags::hiargs|crates/core/flags/hiargs.rs:1269: found hostname for hyperlink configuration: cislo5
rg: DEBUG|rg::flags::hiargs|crates/core/flags/hiargs.rs:1279: hyperlink format: ""
rg: DEBUG|rg::flags::hiargs|crates/core/flags/hiargs.rs:174: using 1 thread(s)
rg: DEBUG|ignore::gitignore|crates/ignore/src/gitignore.rs:393: opened gitignore file:
/home/michael/.config/git/ignore
rg: DEBUG|globset|crates/globset/src/lib.rs:453: built glob set; 1 literals, 0 basenames, 0 extensions, 0
prefixes, 1 suffixes, 0 required extensions, 0 regexes
rg: TRACE|grep_regex::matcher|/project/crates/regex/src/matcher.rs:66: final regex: "(?:(?:(?m:^)(?:import <
/dev/null ))|(?: (?m:^)(?:from)))"
rg: TRACE|grep_regex::literal|crates/regex/src/literal.rs:75: skipping inner literal extraction, existing regex
is believed to already be accelerated
Related #2859
@claude This is a significant issue with Claude Code. It unnecessarily consumes tokens and causes confusing behavior, as Claude Code fails to identify the error from a simple issue and mistakenly attempts to use grep or find.
It is still not repaired.
> Try this command: echo "A|B"
● Bash(echo "A|B")
⎿ A < /dev/null | B
● The command executed successfully, but the output shows that the pipe character | was
interpreted as a shell pipe operator, creating a pipeline where:
- A < /dev/null tried to read from null input
- B was treated as a command (which doesn't exist)
The shell expanded the command differently than expected. If you want to literally print
"A|B", you can use single quotes:
● Bash(echo 'A|B')
⎿ A < /dev/null | B
● Interesting - even with single quotes, the output is being modified. This appears to be
related to the bash environment configuration or the tool execution environment
processing pipe characters. The literal string "A|B" is being transformed into "A <
/dev/null | B".
This seems fixed?
> Try this command: echo "A|B"
● Bash(echo "A|B")
⎿ A|B
● The command output A|B.
Does it work for you, now?
This issue has been inactive for 30 days. If the issue is still occurring, please comment to let us know. Otherwise, this issue will be automatically closed in 30 days for housekeeping purposes.