[BUG] Shell script occasionally fails due to crazy escaping
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [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?
I have not found a way to reliably reproduce this, but from time to time, Claude Code submits wildly incorrect code to the shell even though the commands are printed with completely the right syntax.
Here is a captured example:
⏺ Bash(for f in $(find . -type f -exec tail -c 1 {} \; -print | grep -v '^$');
do awk 1 "$f" > tmp && mv tmp "$f"; done)
⎿ Error: /opt/homebrew/bin/bash: eval: line 1: syntax error near unexpected
token `('
/opt/homebrew/bin/bash: eval: line 1: `for f in \$ ( find . -type f -exec
tail -c 1 \{\} \; -print < /dev/null | grep -v \^\$ ) ; do awk 1 '' > tmp
&& mv tmp '' ; done'
(I had separately tried to tell Claude to not use find in command substitutions but that's tangential here.)
You'll notice that not only has Claude inserted a spurious </dev/null (which I think is already reported multiple times, though I could quickly find only https://github.com/anthropics/claude-code/issues/4315) but also, the dollar sign in the command substitution has been escaped and weirdly followed by a space.
What Should Happen?
Command lines should not be weirdly escaped when sent to the shell. It would probably be better if Claude Code showed _exactly_ what it's going to try to run so that users who understand this language can intervene and correct (including then any spurious additions of </dev/null etc).
Error Messages/Logs
Error: /opt/homebrew/bin/bash: eval: line 1: syntax error near unexpected token `('
Steps to Reproduce
Like I wrote above, I have not been able to reliably reproduce this. It seems to happen in spurts and then go away again. Perhaps there is something in Claude's internal state which triggers this behavior?
Performing a task where you need Claude Code to do things in the shell seems to exhibit this sooner or later.
Claude Model
Sonnet (default)
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
1.0.110 (Claude Code)
Platform
AWS Bedrock
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
This is probably just another aspect of a more fundamental problem around how Claude Code runs commands. I have found related bugs such as
- https://github.com/anthropics/claude-code/issues/3839 which is however specifically about problems with single-quoted strings containing pipe symbols
- https://github.com/anthropics/claude-code/issues/2941 which is however specifically about exclamation marks (and not only in shell, but in any language). I have observed weird backslashing of exclamation marks in shell snippets, too.
- https://github.com/anthropics/claude-code/issues/841 which seems overtly broad but thematically similar, in that it complains about the amount of unnecessary backslashes
- The insertion of
</dev/nullredirections is something I have seen in multiple bug reports, but see e.g. https://github.com/anthropics/claude-code/issues/4315
19 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
I frequently encounter bash tool errors like this across my two main environments (macOS & RHEL). When I asked Claude to search my conversation history for examples to add to this issue, it ironically hit this exact error:
This example error (1 type of many) contains
$(...)(command substitution) and[[ ]](test construct), and has occurred many times for me, so perhaps it is one way to reproduce the error. Issue can be annoyingly worked around by instructingclaudeto not use the bash tool and instead write code to a script & run from disk; however, since this issue is so frequent for me, this issue slows me down significantly.Environment 1 (macOS): Darwin 24.6.0, Terminal.app, Node v24.10, Claude Code v2.0.34, Model: claude-sonnet-4-5-20250929
Environment 2 (RHEL): RHEL 8.4 (Ootpa), Kernel 4.18.0-305.el8.x86_64, SSH, GNU bash 4.4.19(1)-release (x86_64-redhat-linux-gnu), Node v22.17, Claude Code v2.0.34, Model: claude-sonnet-4-5-20250929
EDIT: To clarify, when I run the command it says it is trying to run, it works perfectly. It is also identical in the script file it writes to disk, which also works. I am not sure why it errors so often when the code appears to be fine.
Additional Reproduction Case: Variable Expansion Fails in For Loop
I encountered a similar issue where variable expansion completely fails in a for loop when combined with certain commands.
Command Attempted
Expected Behavior
Actual Behavior
The variable
$pr_numis not expanded in theechocommand.Trace Output (set -x)
When enabling command tracing with
set -x, the actual executed commands reveal the issue:Key observations:
pr_num=1736echo '=== PR # ==='instead ofecho "=== PR #$pr_num ==="gh pr viewbecomes empty string:gh pr view ''instead ofgh pr view 1736Interesting Finding
The same for loop works correctly when simplified:
Output:
The issue only appears when combining the for loop with certain commands like
gh,jq, or complex pipelines.Environment
Note: This issue appears to affect multiple shells. The original report was from bash environments (macOS and RHEL), and I've reproduced the same behavior in my zsh environment. This suggests the problem lies in how the Bash tool processes command strings before passing them to the shell, rather than in the shell itself.
This trace output might help identify where the escaping/quoting transformation is occurring in the Bash tool implementation.
This issue should NOT be auto-closed as a duplicate.
The duplicate candidates (#3839, #1132, #4315) all describe the
< /dev/nullinjection problem. However, this issue (#7387) is about a different problem: variable expansion failure and quote transformation.Evidence from reproduction:
The issue is still reproducible as of Claude Code v2.0.55:
Expected:
Actual:
The variable
$pr_numis not expanded. This occurs when combining a for loop with a pipeline. The same loop without a pipeline works correctly.This is a distinct bug from the
< /dev/nullinjection issue and should be tracked separately.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.
This is indeed still very much an issue. It is rather annoying that you continually require assurances of this. It should be easy to see that there are many bug reports about this and related issues, and you should have plenty of ways to reproduce this yourselves.
Yes, any solution/workaround for this? - very frequently I get things like this:
I tried to add this prompt but CLAUDE just ignores it:
Verification Report
I verified that the following issues have been resolved in Claude Code v2.1.7.
1. Original issue (@Buggy-McBugFace)
The
parse error near '('error when using command substitution$(...)and[[ ]].Test command:
Result:
2. My reported issue
Variable expansion failing in for loops.
Test command:
Result:
3. @rgaufman's reported issue
Syntax errors in for loops with complex regex patterns. I could not reproduce this in my environment, but it may be a different issue from the original report and my report.
Suggestion
The original issue and my reported issue appear to be resolved in v2.1.7. If @rgaufman's issue persists, it may have a different root cause and should be reported as a new issue.
I suggest this issue can be closed.
Adding a noise comment to avoid having this closed.
I can't reproduce if I disable oh-my-zsh, so it seems that is messing with Claude in some way. But I added hooks to stop it doing too much complex inline stuff and instead use skills and helper methods I created for our projects.
Confirmed: this bug caused
rm -rf /*to execute on our systemsWe traced an orphaned
rmprocess that was attempting to delete/back to Claude Code v2.1.69. The root cause is the same shell-quote tokenization bug described in this issue.Minimal Reproduction
Run this via the Claude Code Bash tool:
What happened to us
Claude generated a command roughly equivalent to:
Because the command contained
| head -200, Claude Code took the pipe-aware quoting path (DsD), which tokenizes the command usingshell-quote'sparse(). This function expands${CLEAN_DIR}against Node.js'sprocess.env— where the variable obviously doesn't exist — so it silently expands to an empty string.The actual command executed was:
The
rm -rf /*ran and worked its way through the filesystem for several minutes before we found and killed the orphaned process.Root Cause (from binary analysis of v2.1.69)
The Bash tool has two quoting paths:
etD(no pipe) — wraps the command in single quotes:eval '...'. Shell variables like${VAR}are preserved as literal text and correctly expanded by
evalat runtime. Safe.DsD(command contains|) — tokenizes the command viashell-quote.parse(), splits tokens at the pipe to insert< /dev/null, reconstructs, then re-quotes.
shell-quote.parse()expands${VAR}references againstprocess.envduring tokenization. Any variable that only exists within the bash command itself (e.g., set via
VAR=x && ...) is not inprocess.envand silently expands to empty string. Dangerous.The key code (deobfuscated from the binary):
Note: the function has guards for backtick and `
$(command substitution, but **no guard for${VAR}parameter expansion**. Inline variable assig&&` are a common shell pattern that this code silently corrupts.nments chained with
Impact
This is not a cosmetic quoting issue — it is a data loss / security vulnerability. Any command that combines:
VAR=x && ...)${VAR}or$VAR)|)...will have the variable silently expanded to empty string before bash ever sees it. When combined with glob patterns and destructive commands like
, the results can be catastrophic.rm -rf
Adding a noise comment to prevent this from being closed.
Hey everyone still tracking the "crazy escaping" issues. I recently encountered a wild, real-world side effect of this exact shell-quote bug (specifically the ! being silently escaped to \!) and wrote a full post-mortem about it.
In my case, this escaping behavior silently corrupted a Tauri auto-updater signing key. I used Claude Code to generate the key, and because the Bash tool processes inputs through shell-quote, it silently injected a backslash into my password (mypassword! became mypassword\!). I never saw it, but the key was generated with it.
It worked flawlessly for 20 releases because Claude Code consistently applied the same buggy escaping every time I signed a release. However, when a recent update fixed this escaping behavior, my pipeline broke—the "correct" password no longer matched the key that was generated with the bug!
If anyone is currently digging into how shellQuoting.ts mutates commands or dealing with corrupted interactive CLI inputs, here is the full debugging story:
https://max.nardit.com/articles/the-invisible-backslash
A great example of Hyrum's Law in action where fixing the quoting behavior broke a downstream workflow.
Adding a noise comment to prevent this from being closed.
Adding a noise comment to prevent this from being closed.
Adding a noise comment to prevent this from being closed.
Adding a noise comment to prevent this from being closed.
Adding a noise comment to prevent this from being closed.
Adding a noise comment to prevent this from being closed.