[BUG] Edit(.git/**) permission doesn't allow git to write .git/config in sandbox

Resolved 💬 19 comments Opened Dec 6, 2025 by lior-airis Closed Apr 4, 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?

When Edit(.git/**) is added to permissions.allow in settings, git commands running in the sandbox still cannot write to .git/config. This occurs during git push --set-upstream when git tries to save the tracking branch configuration.

According to issue #10377, Edit() allow rules should extend filesystem write permissions for bash commands in the sandbox. However, this doesn't appear to work for .git/ directories.

Key finding: Running the same command with dangerouslyDisableSandbox: true works without error, confirming this is a sandbox permission issue.

What Should Happen?

With Edit(.git/**) in the permissions allow list, git commands should be able to write to .git/config without errors.

Error Messages/Logs

# Sandboxed (fails):
$ git push --set-upstream origin branch-name
branch 'branch-name' set up to track 'origin/branch-name'.
To https://github.com/user/repo.git
   abc1234..def5678  branch-name -> branch-name
error: could not write config file .git/config: Operation not permitted
error: could not write config file .git/config: Operation not permitted

# Unsandboxed (works):
$ git push --set-upstream origin branch-name
branch 'branch-name' set up to track 'origin/branch-name'.
Everything up-to-date

Note: The push itself succeeds even when sandboxed, but the local config write fails.

Steps to Reproduce

  1. Enable sandbox in ~/.claude/settings.json:
{
  "sandbox": {
    "enabled": true,
    "autoAllowBashIfSandboxed": true
  },
  "permissions": {
    "allow": [
      "Edit(.git/**)"
    ]
  }
}
  1. Restart Claude Code
  1. Run git push --set-upstream origin <branch> via Claude Code
  1. Observe the push succeeds but .git/config write fails with "Operation not permitted"
  1. Run the same command with dangerouslyDisableSandbox: true - it works without error

Configuration Used

{
  "sandbox": {
    "enabled": true,
    "autoAllowBashIfSandboxed": true,
    "network": {
      "allowLocalBinding": true
    }
  },
  "permissions": {
    "allow": [
      "Edit(.git/**)",
      "Edit(.claude/**)"
    ]
  }
}

Claude Model

Opus (claude-opus-4-5-20251101)

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

Latest (as of 2025-12-06)

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

  • The push to remote succeeds; only the local .git/config write fails
  • Workaround: Add git:* to excludedCommands (note: requires :* suffix per #10524), but this requires adding Bash(git:*) to permissions.allow to avoid prompts on every git command
  • Related issues: #10377 (documents Edit() should work for bash writes), #11481 (mentions same error but focuses on network)
  • The error is cosmetic but causes confusion and noise in the output

Possible causes:

  • Edit(.git/**) relative path not resolving correctly against the absolute path git uses
  • .git/ being treated specially by the sandbox
  • The permission check happening before path normalization

View original on GitHub ↗

19 Comments

danielorbach · 7 months ago

Happens to me too ☹️

svenssonaxel · 7 months ago

It probably works if Claude uses the Edit tool rather than executing git.

lior-airis · 7 months ago

I think this is a very reasonable use case that should be supported. I should have the option to allow sandbox to write in the .git subfolder if I wanted to.

alexnovak · 7 months ago

As far as I can tell this isn't exclusive to .git - at least on Linux. This can be _really_ annoying for applications that rely on the ~/.cache directory, like bazel (similar to https://github.com/anthropics/claude-code/issues/10221).

My expectation would be that when we add Edit - we can get something that appears as a --bind mount in bwrap, but that doesn't seem to be the case. When I look at the bwrap invocation under the hood, it looks like we only get binds for /tmp/claude, a logs directory, and the current working directory. Using /add-dir also doesn't seem to influence the mounts on bwrap. This is _such_ a great feature to make sure teams are interacting with claude securely, but it needs a few extra knobs.

lior-airis · 6 months ago

This is still a problem :(

andykrantz · 5 months ago

I have this issue as well with version 2.1.17

jamestelfer · 5 months ago

Happens on 2.1.42 still. Can't confirm, but I think there's a default deny that overrides any attempt to allow.

bryan-omalley-motional · 3 months ago

My claude can't even git push --set-upstream now, because all edits to .git/config are blocked with no option for me to give permission in any way I can find?

alexnovak · 3 months ago

IMO this is now solved with more recent changes to sandbox settings. Specifically for git, you can add the directory to filesystem.allowWrite. Docs can be found here.
For my specific issue with cache, I keep ~/.cache in my allowWrite config.

@lior-airis Maybe give this a try on your setup (if you haven't already) and this could be closed?

lior-airis · 3 months ago

I think this is still a problem but I'm closing this anyway because of auto mode which is probably a better way to solve the permissions problem

bryan-omalley-motional · 3 months ago
IMO this is now solved with more recent changes to sandbox settings. Specifically for git, you can add the directory to filesystem.allowWrite. Docs can be found here. For my specific issue with cache, I keep ~/.cache in my allowWrite config. @lior-airis Maybe give this a try on your setup (if you haven't already) and this could be closed?

I already added .git/config to allowWrite, it does not resolve the issue.

danielorbach · 3 months ago

@lior-airis What is the follow-up issue to track when this misbehaviour is resolved?

lior-airis · 3 months ago
@lior-airis What is the follow-up issue to track when this misbehaviour is resolved?

I don't know, sorry. I would recommend using auto mode instead of sandbox in the future.

dewe · 3 months ago
I don't know, sorry. I would recommend using auto mode instead of sandbox in the future.

Auto mode is not available to Pro and Max users (docs).

bryan-omalley-motional · 3 months ago

Auto mode is still a non-deterministic, LLM-as-judge setup.
For many users, programmatic hooks and sandboxing is still the preferred or necessary option.

alexnovak · 3 months ago

@bryan-omalley-motional Apologies if I was too hasty in that recommendation. I'm surprised that it could give access to the cache, but not .git. Can you show your settings? Is this on mac or linux?

bryan-omalley-motional · 3 months ago

This is on MacOS.

{
  "$schema": "https://json.schemastore.org/claude-code-settings.json",
  "awsAuthRefresh": "aws sso login --profile claude-code",
  "env": {
    "AWS_PROFILE": "claude-code",
    "AWS_REGION": "us-east-1",
    "CLAUDE_CODE_USE_BEDROCK": "1",
    "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1",
    "ENABLE_TOOL_SEARCH": "true",
    "DISABLE_NON_ESSENTIAL_MODEL_CALLS": "1",
    "DISABLE_AUTOUPDATER": "1"
  },
  "attribution": {
    "commit": "",
    "pr": ""
  },
  "includeCoAuthoredBy": false,
  "permissions": {
    "allow": [
      "Read(/**)",
      "Read(**/tmp/**)",
      "Write(**/tmp/**)",
      "Edit(**/tmp/**)",
      "Read(**/temp/**)",
      "Write(**/temp/**)",
      "Edit(**/temp/**)",
      "Read(**/.claude/**)",
      "Write(**/.claude/**)",
      "Edit(**/.claude/**)",
      "Bash(diff *)",
      "Bash(gh pr list *)",
      "Bash(gh pr checks *)",
      "Bash(gh run list *)",
      "Bash(gh run view *)",
      "WebFetch(domain:<REDACTED>)",
      "WebFetch(domain:<REDACTED>)",
      "mcp__*",
      "mcp__plugin_context7_context7__*",
      "mcp__plugin_serena_serena__*",
      "Read(~/docs/**)"
    ],
    "deny": [
      "Read(**/.env)",
      "Write(**/.env)",
      "Edit(**/.env)",
      "Read(**/.pre-commit-config.yaml)",
      "Write(**/.pre-commit-config.yaml)",
      "Edit(**/.pre-commit-config.yaml)"
    ],
    "ask": [
      "Read(/.*/**)",
      "Write(/.*/**)",
      "Edit(/.*/**)",
      "Read(/**/.*)",
      "Write(/**/.*)",
      "Edit(/**/.*)",
      "Read(**/.claude.json)",
      "Write(**/.claude.json)",
      "Edit(**/.claude.json)",
      "Read(**/.claude/settings*.json)",
      "Write(**/.claude/settings*.json)",
      "Edit(**/.claude/settings*.json)",
      "Read(**/.claude/hooks/**)",
      "Write(**/.claude/hooks/**)",
      "Edit(**/.claude/hooks/**)",
      "Bash(aws *)",
      "WebFetch(domain:docs.ray.io)",
      "WebFetch(domain:arxiv.org)",
      "WebFetch(domain:lancedb.com)",
      "WebFetch(domain:lancedb.github.io)",
      "WebFetch(domain:ai.googleblog.com)",
      "WebFetch(domain:blog.google)",
      "WebFetch(domain:blog.research.google)",
      "WebFetch(domain:scholar.google.com)",
      "WebFetch(domain:docs.cloud.google.com)"
    ],
    "defaultMode": "plan"
  },
  "model": "us.anthropic.claude-sonnet-4-6",
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash|mcp__dhub__execute_sql|mcp__ide__executeCode",
        "hooks": [
          {
            "type": "command",
            "command": "bash ~/.claude/hooks/block-destructive-cmds.sh"
          }
        ]
      }
    ]
  },
  "statusLine": {
    "type": "command",
    "command": "bash ~/.claude/status_lines/usage_display.sh"
  },
  "enabledPlugins": {
    "context7@claude-plugins-official": true,
    "serena@claude-plugins-official": true
  },
  "sandbox": {
    "enabled": true,
    "autoAllowBashIfSandboxed": true,
    "allowUnsandboxedCommands": false,
    "network": {
      "allowedDomains": [
        <REDACTED>
      ],
      "allowLocalBinding": true
    },
    "filesystem": {
      "allowWrite": [
        "~/.cache/trunk/**/*",
        "~/docs/**/*",
        ".git/config"
      ],
      "denyWrite": [
        "**/.aws/**/*",
        "**/.env",
        "**/.aiexclude/*",
        "**/.gemini/*",
        "**/.git/hooks",
        "**/.trunk",
        "**/trunk"
      ],
      "denyRead": [
        "**/.aws/**/*",
        "**/.env",
        "**/.aiexclude/*",
        "**/.gemini/*"
      ],
      "allowRead": [
        "~/.cache/trunk/**/*",
        "~/docs/**/*",
        ".git/**/*"
      ]
    }
  },
  "alwaysThinkingEnabled": true,
  "effortLevel": "medium",
  "promptSuggestionEnabled": false,
  "autoUpdatesChannel": "stable",
  "skipDangerousModePermissionPrompt": true,
  "teammateMode": "in-process"
}
alexnovak · 3 months ago

I'll eat my words. I couldn't poke at the implementation in macos, but in linux I was able to poke at the mounts that it adds at the end.

--tmpfs /etc/ssh/ssh_config.d
--ro-bind /dev/null /workspace/.claude/settings.json
--ro-bind /workspace/.claude/settings.local.json /workspace/.claude/settings.local.json 
--ro-bind /dev/null /workspace/.claude/skills 
--ro-bind /dev/null /workspace/.gitconfig 
--ro-bind /dev/null /workspace/.gitmodules 
--ro-bind /dev/null /workspace/.bashrc 
--ro-bind /dev/null /workspace/.bash_profile 
--ro-bind /dev/null /workspace/.zshrc 
--ro-bind /dev/null /workspace/.zprofile 
--ro-bind /dev/null /workspace/.profile 
--ro-bind /dev/null /workspace/.ripgreprc 
--ro-bind /dev/null /workspace/.mcp.json 
--ro-bind /dev/null /workspace/.vscode 
--ro-bind /dev/null /workspace/.idea 
--ro-bind /dev/null /workspace/.claude/commands 
--ro-bind /dev/null /workspace/.claude/agents 
--ro-bind /workspace/.git/hooks /workspace/.git/hooks 
--ro-bind /workspace/.git/config /workspace/.git/config

Notably, regardless of your configuration, it _is_ specifically blotting out .git/config. Apologies - I didn't expect that.
It's especially bizarre because the edit tool _does_ seem to be perfectly capable of modifying .git/config. I'm guessing this is to try and prevent data exfiltration if claude was tricked into modifying your upstream?

You could commit some war crimes to sneak around this on linux - like creating a bwrap shim that re-introduces your write mount at the end. I'm not sure how you'd escape this on macos other than doing something extreme - like using jj instead of git.

github-actions[bot] · 3 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.