anthropics / anthropics/claude-code

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

Aperta
#87,833 2 commenti 0 reazioni 0 assegnatari Vedi su GitHub
area:desktop area:security bug has repro platform:macos
Lingua principale
Python
Stelle
145k
Fork
23.1k
Metriche di merge delle PR
Metriche PR in attesa

Descrizione

## 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//Documents/GitHub/` — 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 '…'`
5. 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------ 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.

2. **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.

Guida per i contributori

Nessuna guida per i contributori indicizzata per questo repository

Direzione di ricerca

No source file or test is named. Begin by reproducing the failure with a repo under ~/Documents, a running CLI session, and a new Claude Desktop session; use the stated codesign, ps, and file-read or git commands to verify the identity and access changes. Done means an existing CLI session keeps access when Desktop starts, and a newly unreadable working directory produces a clear macOS privacy diagnostic instead of continuing silently.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
git, macos
Ambito
cli, operating-systems, security
Tipo di issue
Bug
Difficoltà
5/5
Tempo stimato
Più di una settimana
Stato di attività
Attiva
Chiarezza
Abbastanza chiara
Idoneità per principianti
35/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.