[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 None cached
Activity 2 comments · opened Jul 21, 2026

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 ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗