[BUG] WSL2: Sandbox creates empty files/directories in working directory via bwrap mount point leak

Resolved 💬 2 comments Opened Feb 18, 2026 by Cleroth Closed Feb 18, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

Description

On WSL2, Claude Code creates empty files and directories in the working directory every time the sandbox runs a command (every Bash tool call). These are mount-point artifacts from bubblewrap (bwrap) that leak to the host filesystem due to a WSL2 kernel behavior difference from native Linux.

Files created

Every sandbox invocation creates these empty files/dirs in cwd:

From pC6 array:
.gitconfig, .gitmodules, .bashrc, .bash_profile, .zshrc, .zprofile, .profile, .ripgreprc, .mcp.json

From cq1() array:
.vscode/, .idea/, .claude/commands/, .claude/agents/

From git internal deny paths:
.git/hooks/, .git/config

Root cause

The Linux sandbox builder (CD5 in the compiled source) sets up bwrap like this:

  1. --ro-bind / / -- entire filesystem read-only in sandbox
  2. --bind <cwd> <cwd> -- cwd writable for the command
  3. --ro-bind /dev/null <cwd>/.bashrc (etc.) -- deny writes to sensitive dotfiles within the writable cwd

Step 3 is the problem. When the deny target doesn't exist, bwrap needs to create a mount point. On native Linux, this mount point exists only inside the mount namespace and is invisible to the host. On WSL2, bwrap creates a real 0-byte file (or empty directory) on the host filesystem that persists after bwrap exits.

There IS cleanup code (Z78/nq1 tracking + process.on("exit") handler), but:

  • It only runs on process exit, not after each sandbox invocation
  • Files are recreated on the next Bash tool call
  • If the process is killed (SIGKILL, crash, Ctrl+C), cleanup never runs

Suggested fix

For WSL2 (isWslEnvironment() / vYA() is already detected), one of:

  1. Skip deny bind mounts for non-existent paths on WSL -- if the file doesn't exist, there's nothing to protect from being read; the concern about the sandboxed process creating the file is valid but the cure (mount point leak) is worse than the disease
  2. Clean up mount point artifacts immediately after each bwrap invocation rather than only on process exit
  3. Pre-check with existsSync and only apply deny mounts to paths that already exist (this is partially done but the non-existent path handling is what causes the leak)

Environment

  • WSL2 (Linux 6.6.87.2-microsoft-standard-WSL2)
  • bubblewrap 0.9.0
  • Claude Code 2.1.47

What Should Happen?

No extraneous empty files are created.

Error Messages/Logs

Steps to Reproduce

mkdir -p /tmp/bwrap-test
# This creates a real empty file at /tmp/bwrap-test/.bashrc on the HOST:
bwrap --ro-bind / / --bind /tmp/bwrap-test /tmp/bwrap-test \
      --ro-bind /dev/null /tmp/bwrap-test/.bashrc \
      -- ls /tmp/bwrap-test/.bashrc
# Verify it leaked to host:
ls -la /tmp/bwrap-test/.bashrc
# Output: -r--r--r-- 1 user user 0 ... .bashrc
rm -rf /tmp/bwrap-test

To reproduce the Claude Code bug: start Claude Code in any git repo on WSL2, run any Bash command, then check git status --you'll see all the untracked files listed above.

Claude Model

None

Is this a regression?

Yes, this worked in a previous version

Last Working Version

_No response_

Claude Code Version

2.1.47

Platform

Anthropic API

Operating System

Windows

Terminal/Shell

WSL (Windows Subsystem for Linux)

Additional Information

_No response_

View original on GitHub ↗

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