Bash sandbox (bubblewrap) corrupts `!` to `\!` in commands, making the sandbox unusable for agentic workflows
Summary
Enabling the Bash sandbox on Linux corrupts ! to \! in every Bash tool command. The bubblewrap wrapping path quotes commands with a routine that escapes !, but Claude runs commands through bash -c (non-interactive, history expansion off), so the backslash is never removed and reaches the program as a literal. This breaks any command containing !: jq filters with !=, git commit -m 'fix!', conventional-commit feat!: titles, test assertions, passwords. The result is that the sandbox cannot be turned on for real agentic work, even though it otherwise functions correctly.
The same commands are correct with the sandbox off. The corruption is specific to the sandbox (bubblewrap) command path.
Reproduction
Linux with bubblewrap installed:
mkdir -p /tmp/cc-repro/.claude && cd /tmp/cc-repro
cat > .claude/settings.json <<'JSON'
{
"sandbox": { "enabled": true, "failIfUnavailable": true },
"permissions": { "defaultMode": "dontAsk", "allow": ["Bash"] }
}
JSON
claude -p "Run this exact bash command, one tool call, no edits: printf '%s\n' 'alpha!beta' > out.txt" \
--allowedTools Bash --permission-mode dontAsk
cat out.txt
Expected out.txt:
alpha!beta
Actual out.txt:
alpha\!beta
The real-world case that surfaces it constantly is jq:
claude -p "Run this exact bash command: echo '[1,2,3]' | jq -c 'map(select(. != 2))'" \
--allowedTools Bash --permission-mode dontAsk
Expected [1,3]. Actual:
jq: error: syntax error, unexpected INVALID_CHARACTER (Unix shell quoting issues?) at <top-level>, line 1:
map(select(. \!= 2))
Set sandbox.enabled to false and rerun: both commands are correct. CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1 triggers the same corruption, since it forces the bubblewrap path on (#50167).
Affected versions
Reproduced on claude 2.1.150, 2.1.158 (npm latest), and 2.1.159 (npm next), installed via https://claude.ai/install.sh, on Ubuntu 24.04 with bubblewrap. Linux only: the macOS Seatbelt sandbox uses a different command path and is not affected.
Root cause
The bundled shell-quote quote() used on the bubblewrap path escapes ! inside its double-quote-wrapping branch (the branch taken when a token contains both a single quote and whitespace, which is common in jq filters). That escaping is correct for an interactive shell, where ! triggers history expansion inside "...". Claude runs commands through bash -c, which is non-interactive and has histexpand off by default, so ! has no special meaning and the inserted \ is passed through literally.
The non-bubblewrap path uses a different, correct quoting routine, which is why the bug only appears once the sandbox is on.
Why this matters
This is reported here as a sandbox blocker rather than a generic quoting annoyance. The sandbox is the recommended way to let an agent run Bash autonomously, but an agent that cannot run jq, write a commit message, or handle a ! in any argument is not viable for autonomous use. The escaping silently corrupts data rather than failing loudly, so it can write \! into commit messages, file contents, and downstream tools before anyone notices.
Suggested fix
Drop ! from the escape set on the bubblewrap quoting path. Since commands run via bash -c (non-interactive), history expansion is off and ! needs no escaping:
- q.replace(/(["\\$`!])/g, "\\$1")
+ q.replace(/(["\\$`])/g, "\\$1")
Prior reports
This has been filed several times and closed as not-planned or by inactivity, each framed as a quoting bug rather than a sandbox blocker:
- #35701 — shell-quote escapes
!to\!in non-interactive shells, corrupting command data - #32864 — Bash tool escapes
!to\!when adjacent to whitespace - #29210 — Bash tool silently converts
!(0x21) to\!, corrupts arguments to executables - #38921 — invalid Python syntax from escaped characters
- #23740 — breaks
jqand other tools
Related sandbox behavior: #50167 (CLAUDE_CODE_SUBPROCESS_ENV_SCRUB forces the sandbox on), #35986 (sandbox.enabled: false still wraps with bubblewrap).
_This was written by Claude Code on behalf of Maximilian Roos_
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗