excludedCommands only bypasses read sandbox, not write/unlink
Description
sandbox.excludedCommands in .claude/settings.json bypasses the Seatbelt read sandbox for listed commands, but does not bypass write/unlink restrictions. This makes git unusable in sandboxed sessions despite being in excludedCommands, because git requires creating and unlinking lock files (index.lock, HEAD.lock, refs/heads/*.lock, objects/*/tmp_obj_*) as part of normal operations (git add, git commit, git status).
Steps to reproduce
- Create
.claude/settings.json:
{
"sandbox": {
"enabled": true,
"failIfUnavailable": true,
"allowUnsandboxedCommands": false,
"excludedCommands": ["git", "gh"],
"filesystem": {
"denyRead": ["~/"],
"allowRead": ["."]
}
}
}
- Launch a Claude Code session in the project directory.
- Observe that
git logworks (reads bypass sandbox correctly).
- Run
git add <file>— fails:
warning: unable to unlink '.git/objects/xx/tmp_obj_...': Operation not permitted
warning: unable to unlink '.git/worktrees/.../index.lock': Operation not permitted
fatal: unable to write new index file
- Even
git statusleaves behind a staleindex.lockit cannot clean up, blocking all subsequent git operations.
Evidence that excludedCommands works for reads but not writes
| Command | Result | Conclusion |
|---------|--------|------------|
| git log --oneline | Works | Read bypass works — git reads .git/objects/ despite denyRead: ["~/"] |
| git config --global user.name | Works | Read bypass works — git reads ~/.gitconfig |
| cat ~/.gitconfig | Blocked | Non-excluded command correctly sandboxed |
| git add <file> | Fails on unlink | Write/unlink NOT bypassed |
| git commit | Fails on unlink | Write/unlink NOT bypassed |
| touch foo && rm foo (in project dir) | rm fails | Confirms unlink blocked in project dir |
| touch foo && rm foo (in TMPDIR) | Works | Confirms unlink works in temp dirs |
Additional finding: unlink blocked even in allowWrite directories
The sandbox allowWrite (and the write allowOnly list in the command sandbox) permits file creation but not file deletion/unlink in project directories. Unlink only works in temp directories (/private/tmp/). This affects all bash commands, not just excluded ones.
Impact
This makes the sandbox feature incompatible with git worktrees (which need unlink in both the worktree and the main checkout's .git/ directory), and arguably incompatible with git in general since even basic git status leaves orphaned lock files that block subsequent operations.
Expected behavior
Commands listed in excludedCommands should bypass all sandbox restrictions (read, write, and unlink), not just read restrictions. Additionally, allowWrite directories should permit file deletion/unlink, not just creation.
Environment
- macOS 15.4 (Seatbelt sandbox)
- Claude Code CLI (Claude Opus 4.6)
- Tested with git worktrees (but the issue also affects non-worktree repos)
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗