excludedCommands doesn't bypass sandbox, breaking uv and other tools with .git markers

Status Fixed / completed
Reported on v2.1.29
Maintainer reply None cached
Activity 5 comments · opened Feb 2, 2026 · closed Feb 3, 2026

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

  1. Enable sandbox with a custom cache location:
{
  "sandbox": {
    "enabled": true,
    "autoAllowBashIfSandboxed": true,
    "excludedCommands": ["uv"],
    "allowUnsandboxedCommands": false,
    "additionalDirectories": ["/Volumes/Casa"]
  }
}
  1. Run any uv command via Claude Code:
uv add requests --project /path/to/project
  1. Expected: Command runs unsandboxed (per excludedCommands)
  2. 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 .git access
  • Retry only happens if allowUnsandboxedCommands: true
  • Setting that to true has 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 - excludedCommands not respected
  • #10767 - excludedCommands not working for git SSH
  • #13195 - .git directory 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

  • uv is 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

View original on GitHub ↗

5 Comments

olearydj · 8 months ago

Investigation Update from sandbox-runtime

I investigated this issue in the sandbox-runtime repository and found that sandbox-runtime already handles .git correctly.

Key Findings

  1. getDangerousDirectories() explicitly excludes .git from dangerous directories (sandbox-utils.ts:33-38)
  2. Only .git/hooks and .git/config are blocked for writes
  3. No mandatory read restrictions on .git paths in sandbox-runtime
  4. Default denyRead config is empty

Root 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 .git patterns to the denyRead configuration.

What Was Added

Added isGitMarkerFile() utility to sandbox-runtime that can distinguish .git marker 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:

  • Review what paths are being added to filesystem.denyRead
  • Ensure ~/.cache (or user's cache directories) are not being blocked
  • Or explicitly allow .git marker files in cache directories

See: https://github.com/anthropic-experimental/sandbox-runtime/issues/113#issuecomment-3837978318

zanieb · 8 months ago

As a uv maintainer, I agree that I don't think .git marker files should be treated the same as .git directories.

olearydj · 8 months ago

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:

{
  "permissions": {
    "allow": [
      "Read(//path/to/uv-cache/**)",
      "Edit(//path/to/uv-cache/**)"
    ]
  }
}

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 path
  • Read(~/.cache/uv/**) - home-relative path
  • Read(/foo/**) - WRONG: becomes ~/.config/claude/foo/**

This grants sandboxed Bash commands read/write access to the uv cache, including the .git marker files.

Credit to @tomdale for pointing this out in #22628.

olearydj · 8 months ago

Closing as resolved via Read()/Edit() permission rules - see comments above for the solution.

The excludedCommands behavior may still be a separate bug worth investigating, but the permission approach is the recommended solution since it:

  • Keeps commands sandboxed (more secure)
  • Only grants access to specific paths needed
  • Doesn't require allowUnsandboxedCommands: true
github-actions[bot] · 8 months ago

This 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.