[BUG] Bash Tool JQ Command Escaping Issue

Status Closed — not planned
Maintainer reply None cached
Activity 11 comments · opened May 16, 2025 · closed Dec 12, 2025

Environment

  • Platform (select one):
  • [x] Anthropic API
  • [ ] AWS Bedrock
  • [ ] Google Vertex AI
  • [ ] Other: <!-- specify -->
  • 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

  1. Create a JSON file with an entries array structure:

``json
{"entries": [{"title": "test"}, {"title": "example"}]}
``

  1. Execute a jq command using the Bash tool with array filtering:

``ClaudeExecStepOutput
Bash('jq \'[.entries[]|select(.title=="test")]|length\' file.json')
``

  1. 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.

View original on GitHub ↗

11 Comments

JakobBruenker · 1 year ago

I get the same issue trying to use | with jj (as in jj new -r 'all:(merge-|testing-branch)')

avdaredevil · 1 year ago

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 |
| ---- | ------- |
| !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>

sthaber · 1 year ago

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.

savchenko · 1 year ago

Can confirm, the issue is frequent:
Error: jq: error: syntax error, unexpected '/' (Unix shell quoting issues?) at <top-level>, line 1:

mach3 · 1 year ago

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.

camerondavison · 1 year ago
adamtaylor13 · 1 year ago

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...
     ...                              ^
github-actions[bot] · 10 months ago

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.

benvanik · 9 months ago

I'm hitting issues with this (2.0.34). Nearly every jq command claude tries to use has a pipe and so requires permission.

github-actions[bot] · 8 months ago

This issue has been automatically closed due to 60 days of inactivity. If you're still experiencing this issue, please open a new issue with updated information.

github-actions[bot] · 8 months ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.