[BUG] Sandbox recursively enumerates workspace into nested node_modules → unbounded memory → OOM (still repros on 2.1.195; cf #27863)
Sandbox mode (sandbox.enabled: true) makes claude recursively enumerate the workspace on the first conversation turn and descend into deeply-nested node_modules, growing RSS without bound until the kernel OOM-kills it.
Previously reported in #27863 (auto-closed as stale / not planned) — still reproduces on 2.1.195, and I traced the exact mechanism with strace.
Environment
- Claude Code
2.1.195, Ubuntu 24.04, 32 GB RAM,swap=0 sandbox.enabled: truecwdcontains a large JS monorepo with deeply-nestednode_modules(tens of GB of directory entries)
Repro
sandbox.enabled: true,cwd= a repo with a big nestednode_modules.- Start
claude, send any trivial message (e.g.test). - The main
claudeprocess spins (R) and RSS climbs ~2–2.5 GB/min:~290 MB → 3 GB → 5 GB → 7.75 GB …→ OOM. Withswap=0the OOM killer takessshdand the box becomes unreachable (matches the "system freeze / no errors in debug logs" reports).
Root cause (strace) — pure directory-metadata walk, no file contents read
strace -f -c -p <pid> (3s):
% time calls syscall
12.12 20240 getdents64
8.04 10106 openat
4.98 10106 close
(47% futex / 26% epoll_pwait2 = the spinning event loop)
/proc/<pid>/io over the same window: read_bytes: 0 (rchar advanced ~8 bytes)
Sample openat targets (genericized) — note the node_modules-inside-node_modules recursion:
openat(AT, "<repo>/node_modules/@scope-a/<pkg>/node_modules/@scope-b/<pkg>/node_modules/@scope-c/<pkg>/node_modules/<pkg>", O_RDONLY|O_DIRECTORY)
... (~5,800 such node_modules openat calls per 2-second sample)
This happens before any Bash/tool call — bwrap isn't even spawned yet — so it is claude's own pre-tool workspace enumeration, not the sandbox runtime.
Does NOT fix it (so it's not a misconfig — all tried on 2.1.195)
- Removing all glob patterns from
sandbox.filesystem.denyRead→ still walksnode_modules. sandbox.allowManagedReadPathsOnly: true→ still walks (RSS already 4 GB; ~5,800node_modulesopenat/2s)..claudeignorecontainingnode_modules/→ still walks (strace: 5,437node_modulesopenatin a 2s sample). The enumeration ignores.claudeignoreentirely — directly confirming the "even with ignore files configured" note in #27863.
Avoids it
sandbox.enabled: false→ no walk, no balloon (confirms it is sandbox-specific).- Moving the large subtree out of
cwd, or running from a small dir → fine.
Recovery
The process ignores SIGTERM (it's spinning); only SIGKILL frees the memory.
Multi-agent amplifies it
Each sub-agent enumerates, and the orchestrator respawns a fresh leaking sub-agent every time one is killed (whack-a-mole) — only killing the whole process tree stops it. Likely the same surface as #34568.
Suggested fix
Don't eagerly enumerate the workspace. A single recursive --ro-bind <workspace> expresses "this directory and below" in O(1); deny-paths can be overlaid individually. At minimum, prune node_modules and honor .gitignore / .claudeignore during whatever walk it does.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗