[BUG] Linux sandbox leaks claude-empty-* mount-source directories in the temp dir (never removed)
What's Wrong?
On Linux, the bwrap sandbox leaks an empty claude-empty-XXXXXX directory in the system temp dir (os.tmpdir()) each time it blocks creation of a denied path whose parent does not exist ("Mounted empty dir at X to block creation of Y"). Nothing removes them.
In 2.1.280 the code that builds the bwrap arguments does this (de-minified):
if (blockViaEmptyDir) {
const src = fs.mkdtempSync(path.join(tmpdir(), "claude-empty-"));
bwrapArgs.push("--ro-bind", src, mountPoint);
mountPoints.add(mountPoint); // only the mount point is tracked
...
}
Cleanup at exit walks mountPoints and removes the in-sandbox mount point ([Sandbox Linux] Cleaned up bwrap mount point (dir): ...), then calls mountPoints.clear(). The host-side src directory from mkdtempSync is never recorded, so it stays in the temp dir permanently.
Each leaked directory is empty (4 KB), so the problem is entry count rather than bytes. On one machine it accumulated 2,126 claude-empty-* directories in about 12 hours of normal use (several concurrent sessions and subagents). They add to directory-scan cost and to temp-dir entry growth on hosts where the temp dir is a persistent disk (WSL2, many servers) rather than a tmpfs.
A related smaller leak: srt-mux-<pid>-<n>.sock sockets are unlinked in the mux server's close(), but are left behind when a session is killed without running close(). 1,236 of these accumulated over about 21 hours.
What Should Happen?
Every claude-empty-* directory the sandbox creates should be removed when that sandboxed command finishes, in the same cleanup pass that removes its bwrap mount points. Ideally stale claude-empty-* / srt-mux-*.sock entries owned by dead PIDs would also be reaped at startup.
Steps to Reproduce
- Linux, sandbox enabled, with a write-deny path whose nearest existing ancestor is itself writable (so the sandbox takes the "Mounted empty dir ... to block creation of ..." branch).
- Run sandboxed Bash commands.
ls -d "${TMPDIR:-/tmp}"/claude-empty-*: entries appear and are never removed after the command or session ends.
On this machine they appear in pairs with identical timestamps (two deny paths blocked per sandbox setup). A long session rooted at $HOME created none, so the trigger depends on the working directory and deny-list combination. I haven't isolated the exact path, and debug logs weren't enabled for the sessions that leaked. The missing cleanup is clear from the code above, whatever the trigger.
Claude Code Version
2.1.280
Platform
Linux (WSL2, kernel 6.6.87.2-microsoft-standard-WSL2)
🤖 Generated with Claude Code