Starting a session in Claude Desktop revokes filesystem access from already-running CLI sessions (macOS TCC identity collision)

Status Open
Reported on v2.1.227
Maintainer reply None cached
Activity 3 comments · opened Aug 19, 2026

Summary

On macOS, starting a Claude Code session inside Claude Desktop causes already-running Claude Code CLI sessions to lose read access to their own working tree, if that tree sits in a TCC-protected folder (~/Documents, ~/Desktop, ~/Downloads).

The running session does not fail cleanly. It keeps accepting work while every file read returns EPERM, and git reports fatal: Unable to read current working directory. In my case it burned most of a working session before the cause was found, because nothing in the output points at a permissions problem.

Environment

  • macOS 26.5.1 (25F80), Apple silicon
  • Claude Code CLI 2.1.227, host terminal has Full Disk Access
  • Claude Desktop bundling Claude Code 2.1.229
  • Repo at /Users/<user>/Documents/GitHub/<repo> — inside the TCC-protected Documents folder

Reproduction

  1. Put a repo in ~/Documents. Give the host terminal Full Disk Access.
  2. Start a Claude Code CLI session there. Confirm file reads work.
  3. Leave it running. Open Claude Desktop and start a Claude Code session in the same directory.
  4. Go back to the CLI session. Every read now fails:
  • cat package.json → Operation not permitted
  • git rev-parse --abbrev-ref HEAD → fatal: Unable to read current working directory: Operation not permitted
  • the file-read tool → EPERM: operation not permitted, open '…'
  1. Close the Desktop-hosted session in the UI. CLI access returns immediately.

Step 5 restored access while the Desktop helper processes were still alive (verified with ps), so the trigger is the active session, not the process existing.

Evidence

Both binaries are the same TCC principal.

$ codesign -dv ".../Claude/claude-code/2.1.229/claude.app/Contents/MacOS/claude"
Identifier=com.anthropic.claude-code
TeamIdentifier=Q6L2SF6YDW

$ codesign -dv "~/.local/share/claude/ClaudeCode.app/Contents/MacOS/claude"
Identifier=com.anthropic.claude-code
TeamIdentifier=Q6L2SF6YDW

Claude Desktop launches Claude Code through Apple's responsibility-disclaiming helper.

/Applications/Claude.app/Contents/Helpers/disclaimer \
  ~/Library/Application Support/Claude/claude-code/2.1.229/claude.app/Contents/MacOS/claude …

Disclaiming stops the child inheriting Claude Desktop's TCC attribution, so it becomes responsible for itself — com.anthropic.claude-code, which has no Documents-folder grant. Being non-interactive, it cannot be prompted, so the request is denied.

The denial is scoped to the identity, not the process. The CLI session was covered by its host terminal's grant until the Desktop session created a self-responsible sibling sharing the same identifier. A TCC decision cached against com.anthropic.claude-code would explain why an unrelated, already-running process loses access. I could not read tccd's record to prove this last step — log show returns nothing usable without privileges and the TCC database itself needs Full Disk Access — so treat the caching mechanism as the best-fitting explanation rather than a measured fact. Everything above it is measured.

Ruled out:

  • POSIX permissions, ACLs and file flags. ~/Documents is drwx------ <user> staff with the stock group:everyone deny delete ACL, identical to ~/Desktop, which stayed readable throughout.
  • Filesystem or mount fault. stat on the denied paths succeeds; only open/readdir fail. That split is characteristic of TCC.
  • Path aliasing. The firmlink path /System/Volumes/Data/Users/… is denied identically.
  • Claude Code's own sandbox. Retrying with the Bash sandbox override produced the same EPERM.
  • The host terminal. The affected session ran under a third-party process supervisor, but per the reporter's own observation the same failure occurs with a plain terminal host, and the identity evidence above is host-independent.

Impact

A long-running session silently loses its working tree mid-task and carries on taking instructions. An agent in that state cannot read, test or commit, and the surfaced errors name neither permissions nor the real cause. Anyone keeping a repo in ~/Documents — a common default — hits this the first time they open a Desktop session alongside a CLI one.

Two separate defects

  1. Cross-session access revocation. A newly started session should not be able to remove filesystem access from sessions already running. Either let Claude Desktop's spawned Claude Code inherit Desktop's TCC responsibility instead of disclaiming to the shared identifier, or isolate the two so one session's evaluation cannot affect another's.
  1. No diagnosis on failure. When reads against the session's own working directory start returning EPERM, Claude Code should say so once, clearly, and stop — rather than passing raw errno to the model and continuing. A one-line "the working directory has become unreadable; this is usually a macOS privacy grant" would have saved the entire investigation.

Note

Long-lived sessions outlive their binary: my session was still running 2.1.227, which no longer exists on disk (versions/ held 2.1.223, 2.1.232, 2.1.233, 2.1.235). Whatever fix lands should account for several versions sharing one daemon namespace and one signing identity at the same time.

Aside

I hear there are t-shirts. This one cost me a working session and a wrong diagnosis before the cause turned up, so I would like one.

View original on GitHub ↗

3 Comments

SebastianDickerson · 1 month ago

Follow-up from the same machine, gathered from inside an affected session. Five things this occurrence adds: the direction reverses, the protected-folder control does not hold, the in-app folder-grant tools confirm grants they cannot deliver, a bad working directory poisons every git call, and there is a third binary sharing the signing identity.

Environment — same machine as the report, versions have moved on:

  • macOS 26.5.1 (25F80), Apple silicon
  • Claude Desktop 1.34493.0, hosting Claude Code 2.1.234
  • CLI versions on disk: 2.1.233, 2.1.235, 2.1.237, 2.1.238

1. The direction reverses: the Desktop-hosted session lost access to its own working tree

The original repro has a Desktop session start and an already-running CLI session lose access. Here the disclaimed Desktop child is itself the one denied. A session resumed in Claude Desktop could not read the working tree it was resumed into. Process start times, local:

09:49:11  CLI claude processes start (still running throughout)
12:41:38  editor-hosted @anthropic-ai/claude-agent-sdk binary starts
12:45:10  Claude.app starts
12:45:16  Contents/Helpers/disclaimer -> .../claude-code/2.1.234/.../claude   <- the affected session
12:52:03  first EPERM in that session, against its own working directory

This agrees with the analysis in the report rather than contradicting it: the disclaimed child is responsible for itself as com.anthropic.claude-code, that identity holds no protected-folder grant, and being non-interactive it cannot be prompted. It is worth recording because it means defect 1 is not simply "Desktop wins, CLI loses". The disclaimed child can be the one with nothing, and in that state the Desktop session is unusable for any repo in a protected folder — which is the harder failure, because the Desktop UI is where a user would expect a grant prompt to appear.

2. All three protected folders are denied, not only Documents

The original ruled out ACLs partly on the grounds that ~/Desktop stayed readable. On this occurrence it does not:

~/Desktop    DENIED
~/Downloads  DENIED
~/Documents  DENIED

stat on the denied directories still succeeds while open and readdir fail — the same split as the report. $HOME, ~/.claude and /private/tmp stayed readable throughout. So the denial tracks the whole set of TCC protected-folder grants rather than any single folder, which I think strengthens the identity-level explanation: a per-folder ACL or mount fault would not take all three together and leave the unprotected paths alone.

3. The in-app folder-grant tools report success while access stays denied

This is the one I would fix first, because it actively misleads.

From inside the affected Desktop session I called the folder-grant tools on the working directory. Both returned success:

Folder access granted: /Users/&lt;user&gt;/Documents/GitHub/&lt;repo&gt; Use this exact path with Read/Write/Edit/Grep/Glob.

Every read after that still returned EPERM — through Bash, through the file-read tool, and with the Bash sandbox override applied. The grant layer is not reconciled with TCC, so it confirms a grant it cannot deliver.

The cost is concrete: the success message is evidence, and it is wrong. Acting on it, I concluded the grant had landed and that the cause lay somewhere else, and spent the next several steps looking in the wrong place. The grant call should verify itself with a real read of the granted path and report the failure when the read fails.

4. Moving a session into a denied directory poisons every git invocation, whatever its target

The change_directory call succeeded and moved the session's working directory into the denied path. After that every git command failed with

fatal: Unable to read current working directory: Operation not permitted

including git -C <a readable path>, because git calls getcwd before it honours -C. Prefixing cd / restores enough for non-git commands to run.

Minor next to the rest, but it points the same way as defect 2: the failure presents as a git or repository fault, and neither the message nor the exit code mentions permissions.

5. A third binary shares the signing identity, from a channel users do not know they installed

The report covers the Desktop-bundled and CLI binaries. There is at least one more. An editor extension speaking ACP — Zed's, in this case — pulls @anthropic-ai/claude-agent-sdk-darwin-arm64 from npm, and that binary is the same principal:

$ codesign -dv ".../Zed/external_agents/.../@anthropic-ai/claude-agent-sdk-darwin-arm64/claude"
Identifier=com.anthropic.claude-code
TeamIdentifier=Q6L2SF6YDW

$ codesign -dv ".../Claude/claude-code/2.1.235/claude.app/Contents/MacOS/claude"
Identifier=com.anthropic.claude-code
TeamIdentifier=Q6L2SF6YDW

One was running three and a half minutes before the affected session started. I am not claiming it as the trigger — I could not measure that, and I am not able to read tccd's decisions any more than the original report could.

The point is that the namespace in the Note is wider than two installs. Any editor extension shipping the agent SDK adds another instance of the same identity, on its own release cadence, installed transitively through npm, and the user has no reason to know it is there. A fix that reasons about "the CLI and the Desktop copy" will not cover it.

6. Version skew confirmed from the Desktop side

The running Desktop-hosted process is .../claude-code/2.1.234/..., and 2.1.234 is no longer on disk — that directory now holds 2.1.235 alone. Same shape as the Note in the report, observed from the other host: the session outlived its own binary while still running.

SebastianDickerson · 1 month ago

And on the aside in the original report: this is the second session it has cost me, so I will renew the t-shirt request. Happy to take any merch going.

phact · 6 days ago

I just hit this exact issue where claude deskto messes up the terminal where claude code is running's file system access.

cc 2.1.295 and claude desktop 2.31266.0