[BUG] Bash sandbox fabricates phantom unreadable files when a hidden/denyRead path doesn't exist — breaks dotnet/SourceLink builds

Resolved 💬 3 comments Opened Jun 4, 2026 by EvK Closed Jul 8, 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?

Environment

Claude Code 2.1.162
Linux, WSL2 (kernel 6.6.87.2-microsoft-standard-WSL2)
Bash sandbox enabled (bubblewrap)
Summary

The bash sandbox hides certain dotfiles by overlaying /dev/null on them (.gitmodules, .gitconfig, .bashrc, …). When a hidden path doesn't exist in the workspace, the overlay creates a phantom directory entry — a /dev/null character device. Because the sandbox root is mounted nodev, that device node can't be opened, so the phantom file is unreadable (EACCES) rather than absent. Tools that probe-then-read the file fail, even though it legitimately doesn't exist.

Real-world impact

dotnet build of any SourceLink-enabled project (the default for many SDK projects) in a repo without .gitmodules:

error : Error reading git repository information:
Access to the path '.../.gitmodules' is denied.
[.../Microsoft.Build.Tasks.Git.targets(25,5)]
The identical build succeeds outside the sandbox.

Misleading symptom

Under the default multi-node MSBuild, the worker-node error is dropped and the only output is:

Build FAILED.
    0 Warning(s)
    0 Error(s)

(exit 1, no diagnostics). dotnet build -m:1 is required to surface the real error — worth noting because the default failure mode gives zero clue.

Root cause

The file-hiding mechanism (overlaying /dev/null on each mask-list path) is applied regardless of whether the target exists. For a non-existent target the bind-mount materializes a phantom char device, and on the nodev sandbox root it's unreadable. Net effect: "file absent" (which tools handle gracefully) becomes "file present but unreadable" (which they don't). Note also that masked files do not read as empty here — the documented "empty file" semantics aren't achieved on a nodev mount; reads return EACCES.

What Should Happen?

Suggested fix

stat each mask-list path first and skip the overlay for paths that don't exist on the real filesystem (only mask extant files). Alternatively, mask via an empty regular file so reads return empty/EOF instead of EACCES.

Error Messages/Logs

Steps to Reproduce

Minimal repro

In a sandboxed bash session, in any directory with no .gitmodules:

$ ls -la .gitmodules
crw-rw-rw- 1 nobody nogroup 1, 3 ... .gitmodules    # phantom /dev/null device
$ cat .gitmodules
cat: .gitmodules: Permission denied

Expected: No such file or directory.

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.162 (Claude Code)

Platform

Anthropic API

Operating System

Ubuntu/Debian Linux

Terminal/Shell

WSL (Windows Subsystem for Linux)

Additional Information

_No response_

View original on GitHub ↗

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