Sandbox read-denies .git/config.worktree, breaking all git commands in repos with extensions.worktreeConfig=true

Status Open
Reported on v2.1.217
Maintainer reply None cached
Activity 1 comment · opened Jul 22, 2026
## ⚠️ AI-assisted report — disclosure This issue was investigated and written with AI assistance (Claude Code, model Claude Opus 4.8). Specifically: 1. Investigation: The root cause was diagnosed empirically in a live Claude Code session driven by the AI — inspecting the /dev/null bind mount over .git/config.worktree, testing whether sandbox.filesystem.allowRead overrides the mask, and confirming git's config-load failure. A separate AI research subagent then searched the web and the anthropics/claude-code issue tracker to find related reports and rule out duplicates. 2. Report authoring: This issue description was drafted by the AI from those findings. A human (the account filing this issue) reviewed and approved the content before submitting. The empirical claims were reproduced on a real system (environment and steps below); please still verify independently.

Summary

On Linux (bubblewrap), the sandbox masks .git/config.worktree (and .git/config.lock) with a read-denied /dev/null bind mount. In any repository that has the extensions.worktreeConfig=true extension enabled — which git worktree and git sparse-checkout enable automatically — git must read .git/config.worktree during config load, so every git invocation fails, including read-only ones like git status / git log / git diff:

warning: unable to access '.git/config.worktree': Permission denied
warning: unable to access '.git/config.worktree': Permission denied
fatal: unknown error occurred while reading the configuration files

The documented behavior is that the sandbox denies only writes to .git/config and .git/hooks in a worktree's shared .git while allowing git commit to update refs/index (see the "Git worktrees" bullet under Filesystem isolation in the sandboxing docs). The observed behavior additionally read-denies config.worktree, which is what breaks git entirely for worktree-config repos.

Environment

  • Claude Code 2.1.217
  • git 2.43.0
  • bubblewrap 0.9.0
  • Linux (Ubuntu), sandbox enabled, filesystem.denyRead: ["/"] with an explicit allowRead allowlist (repro does not depend on deny-root; see below)

Reproduction

  1. In a repo that uses linked worktrees (so extensions.worktreeConfig=true):

``
git config --get extensions.worktreeConfig # -> true
``

  1. Enable the sandbox and run any git command inside the sandbox:

``
git status
``

  1. Observe the unable to access '.git/config.worktree': Permission denied fatal above.

stat shows the file has been replaced by a /dev/null character device owned by nobody:nogroup, created at sandbox/session start:

crw-rw-rw- 1 nobody nogroup 1, 3 .git/config.worktree

What does NOT fix it

  • sandbox.filesystem.allowRead for the exact file path — verified it does not override the mask; the read still returns Permission denied and the file is still the /dev/null device. The .git config/hooks protection sits above the allowRead layer.
  • git -c extensions.worktreeConfig=false ... / GIT_CONFIG_GLOBAL=/dev/null — git reads config.worktree during initial config load, before -c/env overrides apply.
  • Disabling extensions.worktreeConfig on the repo — unsafe when worktrees/sparse-checkout exist: config.worktree holds per-worktree core.worktree / core.bare / core.sparseCheckout that git documents as "must not be shared". Disabling breaks those setups, and Claude Code's own worktree feature re-enables it.

The crux (please confirm)

A /dev/null --ro-bind should make a file read as empty (0 bytes), not return EACCES. Here reads get Permission denied, and that read failure is what kills git. If the masked .git config files were instead empty-but-readable, read-only git (and likely git commit, per the documented worktree write-allowance) would work inside the sandbox with no escape hatch needed. The extra read-deny appears to be the actual defect.

Impact

Anyone using git worktree (increasingly common, and used by Claude Code's own worktree feature) cannot run any git command in the sandbox — not even read-only ones — despite the docs implying git works in worktrees. The only workarounds fully unsandbox git.

Current workarounds (all imperfect)

  • sandbox.excludedCommands: ["git *"] — works, but a glob match unsandboxes the entire shell line (e.g. git x && <evil> runs <evil> unsandboxed too), per #40831 (closed not-planned). Also fully unsandboxes git (fs + network).
  • allowUnsandboxedCommands: true + per-command dangerouslyDisableSandbox retry — classifier-gated per invocation, avoids the standing wildcard, but every git command does a failed-then-retry round trip.
  • sandbox.filesystem.disabled: true — fixes git but drops all filesystem isolation, including denyRead and credentials.files.

Suggested fixes (in preference order)

  1. Present masked .git config files (config, config.worktree, config.lock) as empty-readable instead of read-denied, so git's config load succeeds while writes stay blocked. This keeps git sandboxed and functional in worktrees.
  2. Alternatively, expose the sandbox-runtime allowGitConfig flag as sandbox.filesystem.allowGitConfig (feature request #58178) and ensure it also permits the read of config.worktree.
  3. At minimum, document that repos with extensions.worktreeConfig=true cannot run git in the sandbox, and why.

Related issues / references

  • #25603 (mechanism: bwrap /dev/null bind artifacts break git), closed as dup of #17087 / #17727
  • #29316 (empty stub files created when .git is a file / worktree)
  • #22320 (mkdir .git/hooks fails when .git is a file / worktree)
  • #33657 (.git/worktrees/<name>/ not in write allowlist)
  • #13195 (Edit(.git/**) doesn't let git write .git/config in sandbox; maintainer workaround = excludedCommands)
  • #58178 (feature request: expose allowGitConfig; stale)
  • #40831 (excludedCommands glob unsandboxes the whole shell line; closed not-planned)
  • Docs: sandboxing — Git worktrees
  • git docs: git-worktree CONFIGURATION FILE, git-config extensions.worktreeConfig

This config.worktree-specific root cause does not appear to be captured in the existing issues above — it sharpens #25603 / #29316 by identifying the exact trigger (the worktreeConfig extension) and the read-deny-vs-empty-read distinction.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗