excludedCommands doesn't bypass sandbox, breaking uv and other tools with .git markers
Summary
excludedCommands in sandbox settings doesn't bypass the sandbox as documented. Commands still run sandboxed first, fail, and only retry unsandboxed if allowUnsandboxedCommands: true—which defeats the purpose of a targeted exclusion list.
This breaks workflows for tools like uv (Python package manager) that have .git marker files in their cache directories.
Environment
- OS: macOS 14.x (Sonoma)
- Claude Code version: 2.1.29
- Shell: zsh
Reproduction
- Enable sandbox with a custom cache location:
{
"sandbox": {
"enabled": true,
"autoAllowBashIfSandboxed": true,
"excludedCommands": ["uv"],
"allowUnsandboxedCommands": false,
"additionalDirectories": ["/Volumes/Casa"]
}
}
- Run any
uvcommand via Claude Code:
uv add requests --project /path/to/project
- Expected: Command runs unsandboxed (per
excludedCommands) - Actual: Command runs sandboxed, fails with:
error: failed to open file `/path/to/.uv-cache/sdists-v9/.git`: Operation not permitted (os error 1)
Root Cause
The uv cache contains empty .git marker files (to prevent git from indexing the cache). The sandbox blocks all .git access, including these marker files.
The documented workaround (excludedCommands) doesn't help because:
- Commands still execute sandboxed first
- They fail on
.gitaccess - Retry only happens if
allowUnsandboxedCommands: true - Setting that to
truehas broader security implications (any failed command retries unsandboxed)
Workaround
Users must run uv commands manually outside Claude Code, then have Claude use the venv's Python directly:
/path/to/project/.venv/bin/python -c "..."
This works but defeats the purpose of the autonomous workflow.
Related Issues
- #10221 - Original report of uv cache blocked (closed NOT_PLANNED, no response)
- #10524 -
excludedCommandsnot respected - #10767 -
excludedCommandsnot working for git SSH - #13195 -
.gitdirectory permissions broadly broken
Suggested Fixes
Option A: Make excludedCommands bypass sandbox on first attempt (as documented)
Option B: Allow .git marker files while blocking .git directories (the security concern is repo injection, not empty marker files)
Option C: Add a setting to allowlist specific paths for .git access
Impact
uvis the fastest-growing Python package manager- Many users have sandbox enabled for security
- This creates a hard choice: security (sandbox) vs. functionality (uv)
- The documented escape hatch (
excludedCommands) doesn't work
5 Comments
Investigation Update from sandbox-runtime
I investigated this issue in the sandbox-runtime repository and found that sandbox-runtime already handles
.gitcorrectly.Key Findings
getDangerousDirectories()explicitly excludes.gitfrom dangerous directories (sandbox-utils.ts:33-38).git/hooksand.git/configare blocked for writes.gitpaths in sandbox-runtimedenyReadconfig is emptyRoot Cause
The blocking originates from Claude Code's integration layer, not sandbox-runtime. The issue is likely in how Claude Code configures sandbox-runtime - possibly adding cache directories or
.gitpatterns to thedenyReadconfiguration.What Was Added
Added
isGitMarkerFile()utility to sandbox-runtime that can distinguish.gitmarker files from directories. This infrastructure is available if needed for a fix.Recommendation
The fix needs to happen in Claude Code's sandbox configuration, specifically:
filesystem.denyRead~/.cache(or user's cache directories) are not being blocked.gitmarker files in cache directoriesSee: https://github.com/anthropic-experimental/sandbox-runtime/issues/113#issuecomment-3837978318
As a uv maintainer, I agree that I don't think
.gitmarker files should be treated the same as.gitdirectories.Update: Solution Found
My earlier investigation was incorrect. The solution is to use permission rules with the correct path syntax.
Working configuration - add to
~/.config/claude/settings.json:Critical detail: Use
//(double slash) for absolute paths. A single/means "relative to settings file", not filesystem root.Examples:
Read(//Volumes/Casa/.uv-cache/**)- absolute pathRead(~/.cache/uv/**)- home-relative pathRead(/foo/**)- WRONG: becomes~/.config/claude/foo/**This grants sandboxed Bash commands read/write access to the uv cache, including the
.gitmarker files.Credit to @tomdale for pointing this out in #22628.
Closing as resolved via
Read()/Edit()permission rules - see comments above for the solution.The
excludedCommandsbehavior may still be a separate bug worth investigating, but the permission approach is the recommended solution since it:allowUnsandboxedCommands: trueThis issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.