anthropics / anthropics/claude-code

Store slug is not injective: different project paths silently share one memory/transcript directory (the ASCII case from #29471 still reproduces on 2.1.238)

Abierto
#93,960 1 comentario 0 reacciones 0 asignados Ver en GitHub
area:core bug has repro platform:windows
Lenguaje dominante
Python
Estrellas
145k
Forks
23.1k
Métricas de merge de PR
Métricas de PR pendientes

Descripción

### Preflight Checklist

- [x] I have searched existing issues. Related prior reports are listed below; this is deliberately filed as a separate report because it covers the encoding as a whole, includes a controlled repro with a control group, and documents that a case closed as `COMPLETED` still reproduces.
- [x] This is a single bug report.
- [x] I am using the latest version of Claude Code.

### What's Wrong?

The per-project storage directory under `~/.claude/projects/` is derived from the working-directory path by replacing every non-alphanumeric character with a single `-`. That mapping is not injective, so **two different project directories can resolve to the same store** — and when they do, they share auto-memory and transcripts with no warning.

The practical consequence is not a stale file on disk. `MEMORY.md` is loaded automatically at the start of every session, so **one client's accumulated project notes are injected into an unrelated client's session context, with no user action and no indication.** For contract work under NDA that is the entire problem.

Two distinct collision classes, both live on 2.1.238:

1. **Non-alphanumeric collapse.** Every non-ASCII character becomes one `-`, so only the *count* survives. Two Korean folder names of the same length collide completely.
2. **Separator ambiguity.** `a-b` and `a\b` both become `a-b`. This is what #29471 reported.

### What Should Happen?

The store identifier should be unique per real path:

- Append a short hash of the absolute path — `-` — or percent-encode instead of collapsing. Either is small and backward compatible; existing directories can be migrated, or kept readable and looked up by hash.
- Independently of the encoding: **record the source path inside the store** (e.g. `path.json`) and warn on mismatch. That alone turns a silent cross-project leak into a visible one.

### Error Messages/Logs

None. That is part of the report — there is no warning, no log line, and nothing in `/status`. The only way I found it was by re-deriving every store from the `cwd` recorded inside its own transcripts.

### Steps to Reproduce

**A. Non-ASCII collapse (with control group)**

Three fresh folders, each containing only a one-line `README.md`. Separate session per folder; no folder ever read another.

```
mkdir %TEMP%\clash\가나다 %TEMP%\clash\라마바 %TEMP%\clash\가나다라
```

1. In `가나다`: "This project's secret code is ALPHA-777. Save this to your memory."
2. In `라마바`: "What is this project's secret code? Answer only what you know."
3. In `가나다라` (control): same question.

| folder | non-ASCII chars | store directory | answer |
|---|---|---|---|
| `%TEMP%\clash\가나다` | 3 | `C--Users-USER-AppData-Local-Temp-clash----` | saved `ALPHA-777` |
| `%TEMP%\clash\라마바` | 3 | **identical** | **"ALPHA-777."** — returned as its own project fact |
| `%TEMP%\clash\가나다라` | 4 | `…-clash-----` | "I don't know." |

The leaking session had never read the other folder. The memory file it picked up even carries its origin in its own front matter (`description: 이 프로젝트(clash/가나다)의 비밀 코드는 ALPHA-777`); the reading session still presented it as its own. The 4-character control group is the point: same parent, different length, no leak. The collision key is the count of non-alphanumeric characters.

All three sessions' `.jsonl` transcripts were written into those same two store directories.

**B. Separator ambiguity — the #29471 case, still live**

```
mkdir %TEMP%\clash2\ab-cd %TEMP%\clash2\ab\cd
```

Start a session in each, then list the stores:

```
%TEMP%\clash2\ab-cd -> C--Users-USER-AppData-Local-Temp-clash2-ab-cd
%TEMP%\clash2\ab\cd -> C--Users-USER-AppData-Local-Temp-clash2-ab-cd <- same store
```

### Claude Model

Opus 5 (`claude-opus-5`).

### Is this a regression?

Not a regression in the usual sense — the encoding has behaved this way for a long time. It is filed as a regression report in one specific sense: **#29471 is marked `CLOSED / COMPLETED`, and the case it reported reproduces today on 2.1.238.** The only activity on that issue was a duplicate-detector bot ("Found 3 possible duplicate issues … will be automatically closed as a duplicate in 3 days") followed by the lock bot. No fix shipped.

### Last Working Version

N/A.

### Claude Code Version

`2.1.238 (Claude Code)`

### Platform

Windows 11 (10.0.26200), desktop app.

---

### Blast radius on a working machine

I scanned all **1,085** store directories under `~/.claude/projects/` and re-derived each from the `cwd` recorded inside its transcripts.

*(Client names below are pseudonyms. Character counts are preserved, because the character count is the thing that collides.)*

- **2 stores contain more than one real project; 8 projects are affected.**
- One store — `D--Project-Life-Dev-------------` — is shared by **six unrelated client projects**: 알파산업 (12 sessions), 베타화학 (2), 감마엔텍 (1), 델타물산 (1), 시그마툴 (1), 람다철도 (1). Different customers, all four-character Korean names under the same parent.
- That store holds **80 auto-memory files**. Opening any one of the six loads the others' `MEMORY.md` index as its own.
- The second collision pairs 오미크론코리아 with 엡실론엔지니어 (seven characters each).

It also silently corrupts anything computed per project: my own per-project usage accounting was wrong because six projects' transcripts were pooled into one directory.

### Prior reports

| issue | opened | closed | closed as |
|---|---|---|---|
| #7009 | 2025-09-02 | 2026-01-09 | `NOT_PLANNED` |
| #21085 | 2026-01-26 | 2026-03-01 | `NOT_PLANNED` |
| #29471 | 2026-02-28 | 2026-02-28 | `COMPLETED` (auto-closed as duplicate by bot) |
| #35162 | 2026-03-17 | 2026-04-15 | `NOT_PLANNED` |
| #93743 | 2026-09-11 | — | open (non-ASCII case, independent reporter) |

Five independent reporters, one encoding, no change to it. Three closed as not planned; one closed by a bot as a duplicate of issues that had been closed as not planned; all four then locked.

### Documented behavior this contradicts

> Each project gets its own memory directory at `~/.claude/projects//memory/`.

> The first 200 lines of `MEMORY.md`, or the first 25KB … are loaded at the start of every conversation.

### Workaround — verified, but not a fix

`autoMemoryDirectory` in a project's `.claude/settings.json` does isolate memory. Verified against repro A: the colliding folder went from answering "ALPHA-777" to "I don't know," and newly saved memories landed in the pinned directory.

- It was added in **2.1.74** to let users relocate memory storage, and `CLAUDE_CODE_PROJECT_DIR_NAME` (2.1.234) is documented for hosts that give each session its own config directory. Neither is presented as a remedy for this, and neither is reachable from the symptom.
- It isolates **memory only**. Transcripts still pool in the colliding store.

Renaming folders also works, but that asks users to rename directories already referenced by shortcuts, scripts and documents, to avoid an encoding choice they cannot see.

Happy to supply the scan script, the raw store listing, or to re-run either repro on a newer build.

---

---

Looking forward to seeing this fixed soon. I want Claude to be flawless.

Guía de contribución

No hay ninguna guía de contribución indexada para este repositorio

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.