[BUG] Bash tool incorrectly escapes ! characters — still reproducible as of May 2026

Open 💬 5 comments Opened May 21, 2026 by thomasjahoda

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet (well, actually it has been reported multiple times, e.g. as https://github.com/anthropics/claude-code/issues/17495, but it was closed for inactivity)
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

The Bash tool auto-escapes ! to \! before commands reach the shell. This was previously tracked in #17495 (the only non-duplicate report, now closed) but the bug is still present.

The escaping happens at the transport layer regardless of quoting style, which means it affects not just shell interpretation but any argument passed to a subprocess — SQL, jq filters, awk expressions, language runtimes, etc.

Actual output when running a jq filter with !=:

jq: error: syntax error, unexpected INVALID_CHARACTER at <top-level>, line 1:
select(.status \!= "inactive")

Impact:

  • jq: != and !~ operators become \!= / \!~ → syntax error
  • awk: negation operator ! becomes \! → backslash not last character on line
  • SQL via CLI: WHERE status != 'x' becomes WHERE status \!= 'x' → database syntax error
  • Ruby bang methods: save!, create!, find_or_create_by! break when run inline (originally reported in #17495)

The model generates != and ! correctly — the corruption is introduced entirely by the tool's command-quoting pipeline.

What Should Happen?

The jq filter select(.status != "inactive") should execute correctly and return {"status":"active"} without any modification to the != operator.

Error Messages/Logs

jq: error: syntax error, unexpected INVALID_CHARACTER at <top-level>, line 1:
select(.status \!= "inactive")
                ^
jq: 1 compile error

Steps to Reproduce

Ask Claude Code to run this shell command via the Bash tool:

echo '{"status":"active"}' | jq 'select(.status != "inactive")'

Expected output:

{
  "status": "active"
}

Actual output:

jq: error: syntax error, unexpected INVALID_CHARACTER at <top-level>, line 1:
select(.status \!= "inactive")

The ! in != is escaped to \! by the tool's quoting pipeline before the command reaches the shell. Since bash -c runs a non-interactive shell (history expansion disabled), the backslash is not consumed and passes verbatim to jq as \!=, causing a syntax error.

Claude Model

None

Is this a regression?

Yes, this worked in a previous version

Last Working Version

_No response_

Claude Code Version

latest (as of May 2026)

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

Root Cause

The bundled shell-quote library escapes ! via the regex /(["\\$\!])/g. This is meant to prevent Bash history expansion, but Claude Code invokes commands with bash -c (non-interactive), where history expansion is disabled by default. The backslash is never consumed by Bash and passes verbatim to subprocesses.

Proposed Fix

Remove ! from the escape regex:

- s.replace(/(["\\$!])/g, "\\$1")
+ s.replace(/(["\\$])/g, "\\$1")

If suppressing history expansion is desired, it should be handled at the shell level (set +H) rather than in the transport layer.

Workaround

Use ANSI-C hex escaping: $'\x21' instead of !. This bypasses the transport layer's pre-processing.

Related issues (all closed as duplicates, bug still present)

Supersedes / closes: #17495
Duplicates: #2941, #7387, #23740, #24979, #29210, #32864

This report is a re-open of #23740, which was auto-closed as a duplicate of #10335, but the underlying bug has not been fixed.

(I let Claude generate this issue. Sorry if it's really bad, but what are you doing that you simply auto-close issues that have been reported many times without having a way to re-open them easily...)

View original on GitHub ↗

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