claude-code icon indicating copy to clipboard operation
claude-code copied to clipboard

[BUG] Bash Tool JQ Command Escaping Issue

Open davidwalter0 opened this issue 6 months ago • 2 comments

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

  1. Create a JSON file with an entries array structure:
    {"entries": [{"title": "test"}, {"title": "example"}]}
    
  2. Execute a jq command using the Bash tool with array filtering:
    Bash('jq \'[.entries[]|select(.title=="test")]|length\' file.json')
    
  3. 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.

davidwalter0 avatar May 16 '25 18:05 davidwalter0

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

JakobBruenker avatar May 23 '25 12:05 JakobBruenker

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](https://example.com/demo.gif) | ![Content](https://example.com/content.gif) |
  \```

This could be repurposed for bash as echo "|" >/dev/null; <command> where Claude will run it as echo "| > /dev/null" >/dev/null; <command>

avdaredevil avatar Jun 06 '25 07:06 avdaredevil

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.

sthaber avatar Jul 01 '25 20:07 sthaber

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

savchenko avatar Jul 03 '25 12:07 savchenko

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.

mach3 avatar Jul 06 '25 02:07 mach3

i think this is related to https://github.com/anthropics/claude-code/issues/2859

camerondavison avatar Jul 09 '25 14:07 camerondavison

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

adamtaylor13 avatar Jul 12 '25 13:07 adamtaylor13

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.

github-actions[bot] avatar Oct 10 '25 10:10 github-actions[bot]

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

benvanik avatar Nov 05 '25 21:11 benvanik