[Bug] Sandbox write-deny on `.claude/.cc-writes` prevents git worktree removal after ExitWorktree

Status Open
Reported on v2.1.216
Maintainer reply None cached
Activity 1 comment · opened Jul 21, 2026

Bug Description

# Sandbox write-deny on .claude/.cc-writes makes linked git worktrees unremovable (even via ExitWorktree remove), leaving a .cc-writes stub ## Environment - Claude Code: 2.1.216 - OS: macOS 26.5.2 (Darwin 25.5.0), Seatbelt sandbox (sandbox.enabled: true, auto-allow) - git: 2.53.0 - Worktrees located outside the repo (a sibling dir), created via a WorktreeCreate/WorktreeRemove hook that runs git worktree add/git worktree remove. EnterWorktree/ExitWorktree are used to move the session into/out of the worktree. ## Summary When the sandbox is enabled and the session has entered a git worktree (so the worktree became the session's project root), Claude Code creates <worktree>/.claude/.cc-writes/. That directory is write-denied by the sandbox (it falls under the .claude self-protection). The deny persists after ExitWorktree. Consequently any later attempt to remove the worktree via a sandboxed command fails: `` error: failed to delete '<...>/worktrees/<name>': Operation not permitted error: failed to delete '.git/worktrees/<name>': Operation not permitted ` git deletes everything it can and stops on .cc-writes, leaving an orphaned stub whose only content is <name>/.claude/.cc-writes/ (an empty, mode-700 dir). Crucially, **Claude Code's own ExitWorktree({action: "remove"}) also fails to remove the worktree** for the same reason, so this is not specific to a user's custom removal script — the native teardown path hits the same wall. Running the identical git worktree remove **outside** the sandbox (dangerouslyDisableSandbox) succeeds, confirming the blocker is the sandbox deny, not a file flag or permission (the .cc-writes dir has no uchg/schg flags; its only xattr is com.apple.provenance). ## Reproduction 1. Enable the sandbox (sandbox.enabled: true, auto-allow). 2. EnterWorktree into a new linked worktree. 3. Make any file edit with the Edit/Write tool while in the worktree — this creates <worktree>/.claude/.cc-writes/. 4. ExitWorktree({action: "keep"}) back to the main checkout. 5. From the main checkout, run git worktree remove --force <worktree> as a sandboxed Bash command. → fails with Operation not permitted on the worktree body and .git/worktrees/<name>. Also: repeat 1–3, then ExitWorktree({action: "remove", discard_changes: true}) directly (as the worktree's owner) → **also fails** to remove ("could not remove it"). Direct probes confirming the mechanism: - While entered, touch <worktree>/.claude/.cc-writes/xOperation not permitted. - After ExitWorktree(keep), the same touch from the main checkout still → Operation not permitted (deny persists). - A worktree that was **never entered** has a fully deletable .cc-writes (the deny is scoped to session-registered roots, i.e. exact paths, not a **/.claude/.cc-writes glob). ## Impact - Worktree removal fails and leaves an un-deletable stub (<name>/.claude/.cc-writes/) that accumulates across sessions. - The only reliable cleanup is to run the removal unsandboxed. - Because the native ExitWorktree(remove) path is affected too, there is no in-sandbox way to tear down a worktree the session has entered. ## Suspected root cause .claude/.cc-writes (the Edit/Write staging area, undocumented) is write-protected as part of the .claude self-protection so sandboxed shell commands can't tamper with the harness's write staging. The protection is registered for each session-root .claude (including entered worktrees) but is **not lifted when the worktree is being torn down**, and .cc-writes is never cleaned up on exit. This is the same family as: - #76995 — empty .claude/.cc-writes/ directories are not cleaned up after execution. - #78818 — the sandbox materializes/persists a protected-path artifact (.git/config.lock) in linked worktrees and then breaks later git operations. ## Suggested fixes (any one) 1. On ExitWorktree/worktree teardown, drop the worktree's .cc-writes deny (and/or remove the .cc-writes dir) before/so that the worktree can be deleted. 2. Have ExitWorktree({action: "remove"}) perform the deletion with the harness's own (unsandboxed) privileges, the same way it can create/write .cc-writes. 3. Don't place .cc-writes` inside the worktree at all — stage writes in a stable per-session location outside the working tree (e.g. under the session temp dir), so a worktree never carries an un-removable artifact. 4. When a denied path does not exist / is being removed, deny cleanly without leaving a persistent artifact (mirrors the suggestion in #78818).

Environment Info

  • Platform: darwin
  • Terminal: vscode
  • Version: 2.1.216
  • Feedback ID: 26a80654-b233-4724-acf5-9436fa8f1ac7

Errors

[]

View original on GitHub ↗

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