[BUG] sandbox.enableWeakerNestedSandbox silently forced to false when CLAUDE_CODE_SUBPROCESS_ENV_SCRUB is set (breaks Bash sandbox in cap-dropped nested Docker)
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?
With the native Bash sandbox enabled inside an unprivileged container (cap_drop: ALL), settingsandbox.enableWeakerNestedSandbox: true (the documented workaround for nested Docker) has no
effect as soon as the environment variable CLAUDE_CODE_SUBPROCESS_ENV_SCRUB is set. Every
sandboxed Bash command then dies with:
bwrap: Can't mount proc on /newroot/proc: Operation not permitted
CLAUDE_CODE_SUBPROCESS_ENV_SCRUB silently forces the effective value ofenableWeakerNestedSandbox to false, overriding the explicit user setting, so bubblewrap tries to
mount a fresh /proc (needs CAP_SYS_ADMIN) instead of binding the container's existing one.
Root cause (decompiled from the bundled 2.1.198 binary)
The settings->sandbox-config projection does not just read the flag - it conditionally overrides it:
enableWeakerNestedSandbox: $I() && Xce() ? false : e.sandbox?.enableWeakerNestedSandbox
$I()returns the truthiness ofprocess.env.CLAUDE_CODE_SUBPROCESS_ENV_SCRUB.Xce()returns whether a sandbox binary is resolvable (kDn()-> resolvesbwrapPathor"bwrap").
So when CLAUDE_CODE_SUBPROCESS_ENV_SCRUB is set and bubblewrap is available, the explicitenableWeakerNestedSandbox: true is discarded and replaced with false. The bwrap argument builder
then takes the fresh-/proc branch:
P.push("--unshare-pid"); if(!g) P.push("--proc","/proc"); else P.push("--unshare-user","--bind","/proc","/proc");
// g === effective enableWeakerNestedSandbox
(Note: the flag IS present in the settings schema and the allowed-keys list and is normally forwarded;
the defect is the conditional override, not a missing key. That matches #28954/#65719, which report
the flag as forwarded.)
What Should Happen?
An explicit sandbox.enableWeakerNestedSandbox: true should be honored regardless ofCLAUDE_CODE_SUBPROCESS_ENV_SCRUB. If the coupling is intentional, it must be documented, and setting
both should not silently break the Bash sandbox in a cap-dropped container.
Error Messages/Logs
Steps to Reproduce
Same image, same managed settings (sandbox.enabled: true, enableWeakerNestedSandbox: true,failIfUnavailable: true), unprivileged container: cap_drop: ALL, seccomp=unconfined,apparmor=unconfined, host kernel.apparmor_restrict_unprivileged_userns=0. No bwrap wrapper/shim.
- Run a sandboxed Bash command with
CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1set
-> fails: bwrap: Can't mount proc on /newroot/proc: Operation not permitted.
- Run the exact same setup without
CLAUDE_CODE_SUBPROCESS_ENV_SCRUB
-> succeeds: bubblewrap binds the existing /proc and the command runs.
The only variable changed between the two runs is the env var; enableWeakerNestedSandbox: true is
set in both.
Proof the environment supports the intended path: a direct probe works in the same container -bwrap --ro-bind / / --unshare-user --unshare-pid --dev /dev --bind /proc /proc -- echo OK -> OK.
Claude Model
None
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.198
Platform
Anthropic API
Operating System
Other Linux
Terminal/Shell
Other
Additional Information
Is this a regression?
I don't know. #28954 and #65719 (community) both state enableWeakerNestedSandbox is forwarded from
settings, so the conditional override may have been introduced later or only triggers under this
env-var/condition combination. Worth confirming against the version history.
Environment
- Claude Code Version: 2.1.198 (bundled
@anthropic-ai/claude-agent-sdkCLI; also seen on 2.1.199) - Platform: Anthropic API
- OS: Linux (Docker, unprivileged container,
cap_drop: ALL), bundled bubblewrap 0.8.0 - Terminal/Shell: headless SDK
query()/claude -p(no interactive terminal)
Related issues
- #50167 -
CLAUDE_CODE_SUBPROCESS_ENV_SCRUBsilently overridessandbox.enabled: false. Same env
var, same class of undocumented sandbox side effect.
- #28954 -
enableWeakerNetworkIsolationnot wired from settings; notesenableWeakerNestedSandbox
IS forwarded by the translator (consistent with the override, not a dropped key).
- #65719 - lists
enableWeakerNestedSandbox/enableWeakerNetworkIsolationamong the forwarded
keys - again consistent with "forwarded but conditionally overridden".
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗