[BUG] 2.1.216 sandbox regression: default `--cap-drop ALL` breaks all Bash on root installs (apply-seccomp: write /proc/self/uid_map: Operation not permitted)

Status Open
Reported on v2.1.216
Maintainer reply ✓ Yes — bcherny
Activity 3 comments · opened Jul 21, 2026
💡 Likely answer: A maintainer (bcherny, collaborator) responded on this thread — see the highlighted reply below.

Preflight Checklist

  • [x] I have searched existing issues. The closest are #43454, #48304, #73786, #44180 — this is a distinct regression (root install, uid_map write, introduced by 2.1.216's new --cap-drop ALL default); see "Relation to existing issues" below.
  • [x] This is a single bug report.
  • [x] I am using the latest version of Claude Code.

What's Wrong?

Starting with 2.1.216, on a Claude Code install that runs as root on Linux/WSL2 (kernel ≥ 5.12), every sandboxed Bash tool call dies before the user command runs:

apply-seccomp: write /proc/self/uid_map: Operation not permitted

2.1.215 worked fine. 2.1.216 auto-updated overnight and broke all Bash. dangerouslyDisableSandbox: true does not help (the sandboxed pre-flight dies first).

Root cause (from inspecting the bundled binaries): 2.1.216 added --unshare-user --cap-drop ALL to the default bwrap sandbox args (the non-weaker branch). In 2.1.215 that branch was just --dev /dev --unshare-pid --proc /proc — no user namespace, no capability drop.

Independently verifiable regression marker (no decompiler needed — the string is entirely new in 2.1.216):

$ for v in 2.1.212 2.1.215 2.1.216; do
    printf '%s: ' "$v"
    strings -a ~/.local/share/claude/versions/$v | grep -c -- '--cap-drop'
  done
2.1.212: 0
2.1.215: 0
2.1.216: 2

Why it only breaks root installs: the sandbox re-execs the bundled apply-seccomp helper, which creates a nested user namespace and writes 0 0 1 to /proc/self/uid_map. Since Linux 5.12, the kernel's verify_root_map check requires CAP_SETFCAP to map uid 0. But the preceding --cap-drop ALL empties the bounding set, so the euid-0 process re-execed inside the userns holds zero capabilities (CapBnd/CapEff = 0000000000000000) → the uid-0 map write returns EPERM. A non-root install maps e.g. 1000 1000 1 (lower id ≠ 0), so verify_root_map never fires — which is exactly why this is root-specific, and why the closely-related #43454 / #48304 (non-root, failing at the earlier setgroups write) are a different manifestation of the same nested-userns fragility rather than the same bug.

What Should Happen?

Sandboxed Bash should run on root installs, as it did in 2.1.215.

Confirmed fix

Adding --cap-add CAP_SETFCAP to the bwrap invocation (retaining just that one capability through the drop) makes the uid-0 map succeed and Bash works again — verified by replaying 2.1.216's exact wrapper args with --cap-add CAP_SETFCAP added. Alternatively, skip the nested uid_map write (or use an identity map) when geteuid() == 0.

Error Messages/Logs

apply-seccomp: write /proc/self/uid_map: Operation not permitted

Steps to Reproduce

  1. Run Claude Code as root on Linux or WSL2 (kernel ≥ 5.12).
  2. Native Bash sandbox enabled (sandbox.enabled: true).
  3. On ≥ 2.1.216, every Bash tool call fails with the error above. On ≤ 2.1.215 it works. The only delta is the new --unshare-user --cap-drop ALL default args (see the strings marker above).

Workarounds (for other root users hitting this)

  • sandbox.network.allowAllUnixSockets: true in settings — skips the apply-seccomp seccomp step entirely while keeping the rest of the 2.1.216 sandbox (filesystem binds, --unshare-net, --cap-drop ALL). Confirmed working. (Related feature: #44180.)
  • sandbox.enableWeakerNestedSandbox: true avoids --cap-drop ALL, but is silently forced to false when CLAUDE_CODE_SUBPROCESS_ENV_SCRUB is set (#73786), so it is not a reliable workaround.
  • Pin/downgrade to 2.1.215 — but the auto-updater re-bumps to ≥ 2.1.216 on next launch.

Relation to existing issues

  • #43454 / #48304 — same apply-seccomp nested-userns capability restriction, but those fail at the earlier /proc/self/setgroups write on non-root installs and predate the --cap-drop ALL default. This report is the root + uid_map + 2.1.216 --cap-drop ALL variant.
  • #73786 — the CLAUDE_CODE_SUBPROCESS_ENV_SCRUB interaction that makes enableWeakerNestedSandbox a non-workaround here.
  • #44180 — the allowAllUnixSockets knob used as the working workaround.

Claude Model

Opus

Is this a regression?

Yes.

Last Working Version

2.1.215

Claude Code Version

2.1.216

Platform

Claude subscription (Max)

Operating System

WSL2 (Ubuntu) on Windows, kernel 6.6.114.1-microsoft-standard-WSL2, Claude Code running as root.

View original on GitHub ↗

3 Comments

jeongsuho-lawyer · 2 months ago

Sibling regression from the same 2.1.216 sandbox hardening wave, but a different code path / failure / fix — filed separately as #79997.

That one is a non-root install whose workspace/HOME sits under a root-owned directory (/opt/...): the sandbox's ancestor-walk denyWrite mountpoints can't be created, so bwrap aborts with bwrap: Can't mkdir /opt/.claude: Permission denied before any command runs. Same "2.1.215 fine, 2.1.216 breaks all Bash, dangerouslyDisableSandbox doesn't help" shape as here, but the fix is orthogonal to --cap-add CAP_SETFCAP (it's about write-deny mountpoint creation being fail-closed instead of fail-open). Cross-linking for triage.

bcherny collaborator · 1 month ago

Reproduced, and confirmed as a regression at exactly the version you identified.

On the latest release (2.1.233), running as root on Linux with the Bash sandbox enabled (bubblewrap and socat installed), every sandboxed Bash tool call fails before the command runs with exactly:

apply-seccomp: write /proc/self/uid_map: Operation not permitted

A/B testing in the identical environment: 2.1.215 runs sandboxed Bash successfully as root; 2.1.216 fails with the error above, as does 2.1.233 — so the regression was introduced in 2.1.216 and is still present. We also verified your version marker independently against the published binaries: the new capability-drop arguments first appear in 2.1.216 and are absent in 2.1.215, exactly as you found.

Your analysis of why this only affects root installs (the uid-0 mapping in the nested user namespace requiring a capability that the new default drops, on kernels ≥ 5.12) is consistent with everything we observed, and thank you for isolating it from the related non-root reports — this is indeed a distinct manifestation.

Marking as a reproduced regression since 2.1.216. Until a fix ships, the workaround you confirmed (sandbox.network.allowAllUnixSockets: true) is the most reliable option for root installs.

🤖 Generated with Claude Code

marcindulak · 18 days ago

For your information: the label reproduced on this issue does not prevent it from becoming stale and eventually auto-closed. See https://github.com/anthropics/claude-code/issues/87647 for details, and consider upvoting that for visibility.