[BUG] Bash Tool JQ Command Escaping Issue

Resolved 💬 11 comments Opened May 16, 2025 by davidwalter0 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 ↗

This issue has 11 comments on GitHub. Read the full discussion on GitHub ↗