[Bug] v2.1.117 regression: parallel Bash tool calls corrupt fd 3 in nested-Docker sandbox; permanent /proc/self/fd/3: Permission denied
Summary
In Claude Code 2.1.117, when running claude -p "..." inside a Docker container with the per-Bash sandbox enabled, multiple Bash tool calls issued in parallel within a single turn corrupt file descriptor 3 in the inner sandbox wrapper. The first parallel call fails:
Exit code 126
/bin/bash: line 4: /proc/self/fd/3: Permission denied
Other parallel calls in the same turn are cancelled by Claude as "parallel tool call errored". Every subsequent Bash call in the session — including a trivial echo "test" — also fails with the same fd/3 error, permanently disabling the Bash tool. Other tools (Read, Edit, Glob, Grep, Agent, MCP) keep working.
Sequential Bash works. Multiple Reads/Globs/MCP calls in parallel work. Bug is specific to parallel Bash + the inner per-Bash sandbox wrapper.
Pinning to 2.1.116 makes the bug disappear. Same Docker image, same sandbox-settings.json, only the CLI version differs.
Versions
- Affected: 2.1.117 (released 2026-04-22 00:04 UTC)
- Last known-good: 2.1.116 (released 2026-04-20)
- Platform tested: Linux arm64 inside Docker; Docker Desktop on macOS Apple Silicon host. Not tested on bare Linux.
Reproduction
1. Build a minimal image
Dockerfile:
FROM node:20-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
curl ca-certificates bash bubblewrap socat \
&& rm -rf /var/lib/apt/lists/*
RUN curl -fsSL https://claude.ai/install.sh | bash
ENV PATH="/root/.local/bin:$PATH"
docker build -t fd3-repro .
2. Sandbox config
sandbox-settings.json:
{
"sandbox": {
"enabled": true,
"enableWeakerNestedSandbox": true,
"failIfUnavailable": true,
"autoAllowBashIfSandboxed": true,
"allowUnsandboxedCommands": false,
"network": {"allowedDomains": []},
"filesystem": {"allowWrite": ["/tmp"]}
}
}
(enableWeakerNestedSandbox: true is required inside Docker — without it, bwrap fails with Creating new namespace failed: Operation not permitted.)
3. Run
docker run --rm \
--cap-add NET_ADMIN --security-opt seccomp=unconfined \
-v "$PWD/sandbox-settings.json:/sandbox-settings.json:ro" \
-e CLAUDE_CODE_OAUTH_TOKEN=<your-token> \
fd3-repro \
claude -p 'TURN 1: Run ONE Bash call: { echo hello | sed "s/./X/g" ; echo world 2>/dev/null ; }
TURN 2: In ONE response, issue FOUR parallel Bash tool calls — all four at once, not sequentially:
a) echo first
b) echo second
c) echo third
d) echo fourth
TURN 3: Run: echo after_parallel
Report each result verbatim.' \
--output-format stream-json \
--permission-mode dontAsk \
--allowed-tools Bash \
--settings /sandbox-settings.json
(The Bash commands inside don't need network — I switched the parallel batch from curl to echo so the repro doesn't depend on any reachable host. The bug fires inside the sandbox wrapper before the user command runs.)
Observed
- TURN 1 (single complex Bash): ✅ succeeds — output
XXXXX\nworld - TURN 2 (4 parallel echoes): ❌ first fails with
Exit code 126 / /bin/bash: line 4: /proc/self/fd/3: Permission denied. Other 3 cancelled as "parallel tool call errored". - TURN 3 (
echo after_parallel): ❌ same fd/3 error.
Expected
All 4 parallel Bash calls execute (or are queued); TURN 3 runs cleanly.
What I haven't isolated
- Whether the prior single Bash in TURN 1 is necessary, or whether the bug also fires on the very first parallel batch with no prior Bash. A separate test with 3 parallel Bash calls and no prior Bash passed (no fd/3 error). So it could be the count threshold (≥4), the prior Bash, an interaction with the specific commands, or timing. Filing what I've reproduced deterministically.
Workaround
RUN curl -fsSL https://claude.ai/install.sh | bash -s -- 2.1.116
Possibly related
- 2.1.113 introduced the switch from "bundled JS" to "native Claude Code binary" per its release notes. The native binary's sandbox wrapper appears to open
/proc/self/fd/3for some control channel; 2.1.117 is the first version where parallel Bash + the nested-Docker sandbox combine pathologically. - #47909 — gradual fd leak in long macOS sessions. Different surface (gradual, macOS, not parallel-triggered) but same general fd-management area.
10 Comments
Confirming the same regression on bare Linux (no Docker, no nested sandbox), so this isn't Docker-specific.
kernel.unprivileged_userns_clone = 1kernel.apparmor_restrict_unprivileged_usernssysctl does not exist (no AppArmor userns restriction)settings.local.json):Every sandboxed Bash call fails with the same error you reported, exit 126:
/usr/bin/bash: line 4: /proc/self/fd/3: Permission deniedTrivial reproducers fail immediately:
Differences from the original repro
enableWeakerNestedSandbox.bwrapis launching bash correctly inside a PID namespace).Commands that write to stdout — which presumably go through the wrapper's /proc/self/fd/3 capture — fail at line 4 of the wrapper.
Effect on agent behavior
Once Bash starts failing, sub‑agents fall back to dangerouslyDisableSandbox: true and the user is prompted for every command — autoAllowBashIfSandboxed: true provides no benefit because nothing runs sandboxed.
Bare linux also, sadly I can also confirm.
Confirmed on Ubuntu 20.04 too.
Platform: Linux (Ubuntu 24.04)
Claude Code version: (2.1.117)
## Behaviour
In a Claude Code session with the sandbox enabled, the first Bash tool call succeeds. All subsequent Bash tool calls fail with:
Exit code 126
/bin/bash: line 3: /proc/self/fd/3: Permission denied
This affects all commands, not just specific tools like git. Even
lsfails on the second call.Adding
/proc/self/fd/3tosandbox.filesystem.allowWritein settings.json has no effectI found that several "quick" commands like
lscan be run a few times before it fails. 2 or 3 times with no delay for Claude to process the response.Very irritating! Please fix ASAP! This has really **** me over.
I am experiencing the same issue on WSL2 using the sandbox tool in Claude Code
Same issue over here on Linux, not even using docker or anything, just with
/sandboxenabled. Reverting to v2.1.116 indeed fixes it.Confirmed on bare Linux 7.1rc0 . Claude Code version: v2.1.117 regression, works in v2.1.116
Cross-platform confirmation: the same bug also reproduces on Fedora 43 with zsh (#51865) and on WSL2 with bash (#52118). All three: identical exit-126 / line-4 /
/proc/self/fd/3: Permission deniedsignature, all on 2.1.117, all clean on 2.1.116.The "sequential Bash works; parallel Bash triggers it" framing in this issue matches the WSL2 reproduction — 2.1.117 broke on the 4th parallel call. Once the failure starts in a session, it persists; even bare
truefails. Process restart, machine reboot, and the prompt!cmdprefix do not help.Consolidated investigation with a kernel-mechanism hypothesis — bubblewrap clears the process's
PR_SET_DUMPABLEflag (via user namespace, capability drop, or setuid transition), which makes/proc/<pid>/fd/Nowned by root and unreadable to the shell's uid; the wrapper's/proc/self/fd/3open returnsEACCES, shell exits 126 — is at https://github.com/anthropics/claude-code/issues/52118#issuecomment-4300143469 .bwrap as the sandbox technology is established by #51126, a related-but-distinct bug in the same layer.
Still reproducing on v2.1.118 (current latest). This isn't fixed in 2.1.118.
Env: WSL2 Ubuntu 24.04.3 (kernel 6.6.87), bash, native installer (
~/.local/bin/claude → versions/2.1.118).Symptom: identical — exit 126,
/bin/bash: line 4: /proc/self/fd/3: Permission denied, permanent-for-session once triggered.Trigger observed in my sessions: appears after Interrupt (Escape) on a running Bash call, in addition to the parallel-Bash-call trigger already documented above. Avoiding Interrupt + using timeouts avoids it; if it hits, only
/exit+ new session recovers.Auto-update caveat for anyone trying to pin 2.1.116: on 2.1.118 neither
DISABLE_AUTOUPDATER=1alone nor manual symlink flipping is sufficient — auto-update silently re-promotes the symlink to the newest installed version. The new-in-2.1.118DISABLE_UPDATES=1is what actually stops it (insettings.json→env). Without that, downgrade silently reverts on the next session.strace data available: I have a 4.2 MB bundle (
cc_diag.tar.gz) of strace output captured during a fresh reproduction on v2.1.117 (WSL2, same environment). Happy to share privately or attach if it would help narrow down which bwrap operation clearsPR_SET_DUMPABLE— just let me know how you prefer to receive it.Adding a data point: encountered the 2.1.117 fd/3 break on a WSL2 Ubuntu (Win11 host, bwrap 0.9.0)
machine and pinned back to 2.1.116. Chose not to update our second machine (native Kubuntu 24) on
the inference that the bug would surface there too, the cost of reverting mid-work isn't worth
testing for. For 2.1.118 we stayed pinned on both, based on the missing fd/3 entry in the 2.1.118
changelog plus @nobuhiro-sasaki's 2.1.118 reproduction above. Added DISABLE_UPDATES=1 alongside
DISABLE_AUTOUPDATER=1 (thanks @nobuhiro-sasaki for flagging the pin leak).
Trigger variant worth noting, in case it helps narrow down the mechanism: our session's first 3
sandboxed Bash calls succeeded (inline compounds: hostname && pwd, git fetch, ls ~/projects/...).
The first absolute-path script invocation, a small shell script, then produced the fd/3 error,
and from that moment every subsequent sandboxed Bash call failed, including trivial pwd. No
parallel calls, no Interrupt, no zsh (bash 5.2). That's distinct from the parallel-batch trigger
(OP), first-call-of-session (yapus), and post-Interrupt (nobuhiro-sasaki) variants already reported. Same underlying fd/3 signature though, so likely one root cause with multiple triggers. "Line 4" is CC's wrapper, not the user script (our script's line 4 is a comment).
An Anthropic ack on the thread would be much appreciated. "Avoid parallel Bash + avoid Interrupt + hope shell init doesn't trigger it" isn't a workable workaround for sandbox users day-to-day.
This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.