[BUG] Sandbox blocks its own zsh `eval` wrapper — every Bash command fails with exit 126

Open 💬 0 comments Opened Jul 14, 2026 by simocsdk

Bug Description

# [BUG] Sandbox blocks its own zsh eval wrapper — every Bash command fails with exit 126 ## Environment - Claude Code version: 2.1.209 - OS: macOS (Darwin 25.5.0), sandbox backend: Apple Seatbelt (/usr/bin/sandbox-exec) - Login shell: zsh (macOS default) - Install: native CLI, interactive session ## Summary With sandbox.enabled: true and zsh as the user shell, 100% of Bash tool commands fail with exit code 126, including trivial ones like pwd. The sandbox's own static security filter ("Command uses eval or $()/ backticks at command position") rejects the eval-based wrapper that Claude Code itself generates for zsh. The sandbox blocks itself. ## Minimal reproduction 1. macOS machine whose login shell is zsh (the default). 2. ~/.claude/settings.json: ``json { "sandbox": { "enabled": true } } ` 3. Let Claude run any Bash tool command, e.g. pwd. Nothing else is required — reproduces without hooks, permission rules, or plugins. Also reproduces when the sandbox is enabled via the /sandbox UI command with "regular bash permissions". **Verified with hooks disabled:** re-ran the repro with "disableAllHooks": true — the failure is byte-for-byte identical, same decoded eval wrapper in the LogTag. Also tested with "disableAllHooks": false — same result. Hooks are not involved; the block occurs in the sandbox layer before any hook could run. ## Expected behavior The command runs inside the Seatbelt sandbox and succeeds. ## Actual behavior Every command fails immediately: Exit code 126 [BLOCKED — DO NOT RETRY] Command uses eval or $()/ backticks at command position, which is blocked regardless of allowlist. This is a permanent security restriction, not a transient error. text ## Root cause analysis The Seatbelt profile embeds the wrapped command as a base64 LogTag. Decoding it for the pwd repro shows the wrapper Claude Code generates for zsh: setopt NO_EXTENDED_GLOB NO_BARE_GLOB_QUAL 2>/dev/null || true && eval pwd < /dev/null && pwd -P >| /... text The zsh compatibility wrapper places eval in command position **by design**. The sandbox's static analyzer then rejects any command with eval in command position "regardless of allowlist". The two components are mutually exclusive: on zsh, no command can ever pass. Notes: - sandbox.failIfUnavailable: true does not catch this — the sandbox initializes "successfully"; only command execution is broken. - The user-facing effect is that users conclude the sandbox is broken and disable it entirely ("enabled": false), which is a net security regression — the opposite of the feature's intent. ## Workaround Disable the sandbox (sandbox.enabled: false) and rely on permission deny rules + hooks. Untested alternative: launching with SHELL=/bin/bash claude so the zsh wrapper is not used. ## Possibly related (but distinct) issues - anthropics/claude-code#22241 — zsh "operation not permitted" on /tmp/claude-{UID}/cwd-*` (commands still succeeded there; here they hard-fail) - anthropics/claude-code#22109 — zsh cwd-tracking sandbox noise - anthropics/claude-code#28479 — zsh shell initialization handling

Environment Info

  • Platform: darwin
  • Terminal: Apple_Terminal
  • Version: 2.1.209
  • Feedback ID: 966943a8-670b-4291-9d97-464ce5592a8f

Errors

[]

View original on GitHub ↗