[BUG] Sandbox denyWithinAllow creates bind-mounted artifacts in working directory, breaking git operations

Resolved 💬 3 comments Opened Feb 13, 2026 by H4ST3 Closed Feb 17, 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?

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:

  1. 0-byte read-only bind-mounted files named HEAD, config, hooks, objects, refs in the project root — the sandbox interprets paths like <project>/HEAD literally instead of targeting .git/HEAD
  2. .env.example replaced with a /dev/null character device — the denyOnly read rule bind-mounts /dev/null over the real tracked file

These artifacts break multiple git workflows:

  • git format-patch -1 HEAD fails: fatal: ambiguous argument 'HEAD': both revision and filename
  • git stash fails: error: .env.example: unsupported file type; fatal: cannot hash .env.example
  • git diff fails with the same unsupported file type error
  • pre-commit hooks 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

  1. Create a git repo with a .env.example file tracked in git
  2. Start a Claude Code session with sandbox enabled (default on Linux with bwrap)
  3. The sandbox auto-generates denyWithinAllow write rules for git-internal path names (HEAD, objects, refs, hooks, config) at the project root — not inside .git/
  4. The sandbox auto-generates denyOnly read rules for .env.example
  5. Run any Bash tool command to trigger sandbox activation
  6. Observe with ls -la: new 0-byte read-only files and .env.example is now a character device
  7. Attempt any git operation that references HEAD or 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 covered denyWithinAllow paths or auto-generated git-internal path names.
  • #17727 (open): Describes the same --ro-bind /dev/null mechanism 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-verify to bypass pre-commit (crashes on the device node)
  • @~1 instead of HEAD in git commands to avoid filename ambiguity
  • Artifacts disappear when the Claude Code session ends

View original on GitHub ↗

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