claude-code
claude-code copied to clipboard
[BUG] Bash Tool JQ Command Escaping Issue
Environment
- Platform (select one):
- [x] Anthropic API
- [ ] AWS Bedrock
- [ ] Google Vertex AI
- [ ] Other:
- Claude CLI version: 0.2.115 (Claude Code)
- Operating Systems:
- macOS Darwin 24.4.0
- Ubuntu linux
! ~/.claude/local/claude --version
⎿ 0.2.98 (Claude Code)
! lsb_release --version
⎿ LSB Version: core-11.1.0ubuntu4-noarch:printing-11.1.0ubuntu4-noarch:security-11.1.0ubuntu4-noarc
- Terminal: Command line through Claude Code
Bug Description
The Bash tool in Claude Code has a critical escaping bug that corrupts
jq commands containing the pattern .entries[]|. This pattern gets
incorrectly transformed during command preparation, causing stdin
redirection syntax (< /dev/null) to be injected into the middle of
jq expressions, resulting in syntax errors that make many common jq
queries impossible to execute.
Steps to Reproduce
- Create a JSON file with an
entriesarray structure:{"entries": [{"title": "test"}, {"title": "example"}]} - Execute a jq command using the Bash tool with array filtering:
Bash('jq \'[.entries[]|select(.title=="test")]|length\' file.json') - The command fails with a jq syntax error
Expected Behavior
The jq command should execute correctly and return the count of matching entries:
$ jq '[.entries[]|select(.title=="test")]|length' file.json
1
Actual Behavior
The command fails with a syntax error because the pipe character is corrupted:
jq: error: syntax error, unexpected '/' (Unix shell quoting issues?) at <top-level>, line 1:
[.entries[] < /dev/null | select(.title=="test")]|length
The pattern .entries[]| is being transformed to .entries[] < /dev/null | during command escaping.
Additional Context
Root Cause Analysis
Through testing, we discovered that the command escaping mechanism
specifically corrupts the pattern .entries[]| by inserting < /dev/null between the array accessor and pipe. This can be verified
by base64 encoding:
# create file.json
echo '{"entries": [{"title": "test"}, {"title": "example"}]}' > file.json
# What we type:
jq '[.entries[]|select(.title=="test")]|length' file.json
# What gets executed (after decoding base64):
jq '[.entries[] < /dev/null | select(.title=="test")]|length' file.json
Working Examples vs Failing Examples
- ✅ Works:
echo '{"test": 1}' | jq '.test' - ✅ Works:
jq '.entries[0].title' file.json - ❌ Fails:
jq '[.entries[]|select(.title=="test")]' file.json - ❌ Fails:
jq '(.entries[]|select(.title=="test"))' file.json
Verified Workarounds
Workaround 1: Script File
Write("/tmp/query.sh", """#!/bin/bash
jq '[.entries[]|select(.title=="test")]|length' file.json
""")
Bash("chmod +x /tmp/query.sh && /tmp/query.sh")
# Returns correct result: 1
Workaround 2: Base64 Encoding
Write("/tmp/cmd.txt", "jq '[.entries[]|select(.title==\"test\")]|length' file.json")
Bash("encoded=$(cat /tmp/cmd.txt | base64); eval \"$(echo $encoded | base64 -d)\"")
# Returns correct result: 1
Impact
- Prevents data extraction from JSON files with array structures
- Blocks analytics and reporting workflows
- Forces developers to use complex workarounds for basic operations
- Affects any jq query using the common pattern
.array[]|filter
Test Environment
-
Working directory:
~/test/
ls -l file.json
-rw-r--r-- 1 user staff 54 May 16 14:03 file.json
- Node.js based Claude Code (evident from path structure)
Minimal Reproducible Example
# Create test data
Write("file.json", '{"entries": [{"title": "test"}, {"title": "other"}]}')
# This fails:
Bash(jq '[.entries[]|select(.title=="")]|length' file.json)… Cost: $0.0275 (5.5s)
⎿ Error: jq: error: syntax error, unexpected '/' (Unix shell quoting issues?) at <top-level>, line 1:
[.entries[] < /dev/null | select(.title=="")]|length
jq: 1 compile error
# This shows the corruption:
Bash('echo \'jq "[.entries[]|select(.title==\\\"test\\\")]|length" file.json\' | base64')
# Decode reveals: < /dev/null injected into command
Scope
This bug affects a common jq pattern used in many workflows.
I get the same issue trying to use | with jj (as in jj new -r 'all:(merge-|testing-branch)')
It literally happens in any | inside a string. For now if anyone else is running into it, I've added the following guidance in my local CLAUDE.md files:
### ⚠️ CRITICAL: PowerShell/Cmd.exe Pipe Injection Fix
- **PROBLEM**: Your environment injects `/dev/null` before the first pipe in PowerShell/Cmd.exe commands, breaking pipe chains
- **SOLUTION**: Always prefix PowerShell commands containing pipes with a "null consumer" (`"|" >$null;`):
```pwsh
pwsh.exe -NoProf -Command '"|" >$null; ACTUAL_COMMAND_WITH_PIPES'
```
- **Examples**:
```pwsh
# For commands with pipes
pwsh.exe -NoProf -Command '"|" >$null; gh pr list | grep "open"'
# For complex git operations with pipes
pwsh.exe -NoProf -Command '"|" >$null; git log --oneline | head -5'
# For multi-line here-strings with pipes
pwsh.exe -NoProf -Command '"|" >$null; git commit -m @"
This is a multi-line commit message with pipes:
| Demo | Content |
| ---- | ------- |
|  |  |
\```
This could be repurposed for bash as echo "|" >/dev/null; <command> where Claude will run it as echo "| > /dev/null" >/dev/null; <command>
This is affecting my ability use rg as my primary code search tool in Claude Code. It randomly inserts < /dev/null into regex that looks like thing|other|thing|etc, causing the tool to hang for the default timeout of 2 minutes. My horrible workaround is a CLAUDE.md directive to set a 10 second timeout on rg commands.
Can confirm, the issue is frequent:
Error: jq: error: syntax error, unexpected '/' (Unix shell quoting issues?) at <top-level>, line 1:
Another workaround: Use double quotes instead of single quotes for jq commands.
# Instead of this (fails):
jq '.items[] | select(.status != "Done")'
# Use this (works):
jq ".items[] | select(.status != \"Done\")"
The pipe character works normally inside double quotes without any escaping issues.
i think this is related to https://github.com/anthropics/claude-code/issues/2859
This happens in a lot more than just jq as well. Basically anytime you try to use Rail's runner command with a proc:
⏺ Bash(bin/railsd runner "puts 'Total orders: ' + Order.count.to_s; puts 'Orders with non-zero status: ' + Order.where.not(status: 0).count.to_s; puts
'Recent orders:'; Order.limit(5).each { |o| puts \"ID: #{o.id}, Created: #{o.created_at}, Status: #{o.status}, Total: #{o.order_total_cents}\"
}")
Please specify a valid ruby command or the path of a script to run.
Run 'bin/rails runner -h' for help.
/usr/local/bundle/ruby/3.2.0/gems/railties-8.0.1/lib/rails/commands/runner/runner_command.rb:49: syntax error, unexpected '<'
...ers:'; Order.limit(5).each { < /dev/null | o| puts "ID: #{o...
... ^
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.
I'm hitting issues with this (2.0.34). Nearly every jq command claude tries to use has a pipe and so requires permission.