[BUG] Sandbox denyWithinAllow creates bind-mounted artifacts in working directory, breaking git operations
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?
The sandbox enforces denyWithinAllow write rules and denyOnly read rules by creating filesystem artifacts (bind mounts and device nodes) in the project working directory rather than inside .git/. This produces:
- 0-byte read-only bind-mounted files named
HEAD,config,hooks,objects,refsin the project root — the sandbox interprets paths like<project>/HEADliterally instead of targeting.git/HEAD .env.examplereplaced with a/dev/nullcharacter device — thedenyOnlyread rule bind-mounts/dev/nullover the real tracked file
These artifacts break multiple git workflows:
git format-patch -1 HEADfails:fatal: ambiguous argument 'HEAD': both revision and filenamegit stashfails:error: .env.example: unsupported file type; fatal: cannot hash .env.examplegit difffails with the same unsupported file type errorpre-commithooks crash:FatalError: pre-commit failed to diff -- perhaps due to permissions?- Files cannot be removed:
rm: cannot remove 'HEAD': Device or resource busy
What Should Happen?
The sandbox should protect .git/HEAD, .git/objects, .git/refs, .git/hooks, and .git/config — not create identically-named artifacts in the working directory root. The denyOnly read rules should block reads without replacing tracked files with device nodes.
Error Messages/Logs
Bind-mounted files appear in the working directory:
$ ls -la HEAD config hooks objects refs
-r--r--r-- 1 user user 0 Feb 13 10:31 HEAD
-r--r--r-- 1 user user 0 Feb 13 10:31 config
-r--r--r-- 1 user user 0 Feb 13 10:31 hooks
-r--r--r-- 1 user user 0 Feb 13 10:31 objects
-r--r--r-- 1 user user 0 Feb 13 10:31 refs
.env.example replaced with character device node:
$ ls -la .env.example
crw-rw-rw- 1 nobody nogroup 1, 3 Feb 13 08:58 .env.example
$ stat .env.example
File: .env.example
Size: 0 Blocks: 0 IO Block: 4096 character special file
Device: 0,5 Inode: 4 Links: 1 Device type: 1,3
Access: (0666/crw-rw-rw-) Uid: (65534/nobody) Gid: (65534/nogroup)
Mount evidence confirms these are bind mounts, not regular files:
$ findmnt -T HEAD
TARGET SOURCE FSTYPE OPTIONS
<project>/HEAD /dev/sdd[<project>/HEAD] ext4 ro,nosuid,nodev,relatime,...
$ findmnt -T .env.example
TARGET SOURCE FSTYPE OPTIONS
<project>/.env.example none[/null] devtmpfs ro,nosuid,nodev,relatime,...
Git operations that fail:
$ git format-patch -1 HEAD
fatal: ambiguous argument 'HEAD': both revision and filename
$ git stash
error: .env.example: unsupported file type
fatal: cannot hash .env.example
$ git diff --stat
error: .env.example: unsupported file type
fatal: cannot hash .env.example
$ git commit --amend --no-edit
FatalError: pre-commit failed to diff -- perhaps due to permissions?
$ rm HEAD
rm: cannot remove 'HEAD': Device or resource busy
Steps to Reproduce
- Create a git repo with a
.env.examplefile tracked in git - Start a Claude Code session with sandbox enabled (default on Linux with bwrap)
- The sandbox auto-generates
denyWithinAllowwrite rules for git-internal path names (HEAD,objects,refs,hooks,config) at the project root — not inside.git/ - The sandbox auto-generates
denyOnlyread rules for.env.example - Run any Bash tool command to trigger sandbox activation
- Observe with
ls -la: new 0-byte read-only files and.env.exampleis now a character device - Attempt any git operation that references
HEADor touches.env.example— it fails
Relevant sandbox config (auto-generated, visible in tool definitions):
{
"write": {
"denyWithinAllow": [
"<project>/HEAD",
"<project>/objects",
"<project>/refs",
"<project>/hooks",
"<project>/config"
]
},
"read": {
"denyOnly": [
"<project>/.env.example"
]
}
}
Note: These paths are auto-generated by Claude Code — I did not configure them manually. The intent appears to be protecting .git/ internals, but the paths resolve to the working directory root instead.
Is this a regression?
Yes, this used to work
Last Working Version
Unsure of exact version — first noticed in 2.1.32. Related issue #17087 (phantom dotfiles) was closed as completed but this variant persists.
Claude Code Version
2.1.32
Platform
Anthropic API (direct)
Operating System
Linux (WSL2)
Terminal/Shell
Bash
Additional Information
Relationship to existing issues:
- #17087 (closed): Described empty read-only dotfiles (
.bashrc,.gitconfig) created by bwrap — same root cause mechanism but different paths. That fix may not have covereddenyWithinAllowpaths or auto-generated git-internal path names. - #17727 (open): Describes the same
--ro-bind /dev/nullmechanism creating 0-byte files, and notes bwrap doesn't check if files exist before mounting.
Two distinct sandbox mechanisms producing artifacts:
| Mechanism | Config Source | Effect | Files Affected |
|-----------|-------------|--------|----------------|
| denyWithinAllow write | Auto-generated | 0-byte file, ext4 ro bind mount | HEAD, objects, refs, hooks, config |
| denyOnly read | Auto-generated | Character device (1,3 = /dev/null), devtmpfs ro bind mount | .env.example |
Workarounds:
git commit --no-verifyto bypass pre-commit (crashes on the device node)@~1instead ofHEADin git commands to avoid filename ambiguity- Artifacts disappear when the Claude Code session ends
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗