anthropics / anthropics/claude-code

Pinned sidebar section fills up with sessions and cannot be cleared

Aperta
#88,199 0 commenti 1 reazione 0 assegnatari Vedi su GitHub
area:desktop area:ui bug platform:windows
Lingua principale
Python
Stelle
145k
Fork
23.1k
Metriche di merge delle PR
Metriche PR in attesa

Descrizione

### Preflight Checklist

- [x] I have searched [existing issues](https://github.com/anthropics/claude-code/issues?q=is%3Aissue%20state%3Aopen%20label%3Abug) 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?

# Pinned sidebar section fills up with sessions and cannot be cleared

**Environment:** Claude Code 2.1.234, desktop shell 1.32885.1, Windows 11 Home 10.0.26200,
MSIX (Microsoft Store) install, Node 24.18.1. ~500 local sessions, 184 of them pinned.

## What happens

The "Pinned" section in the left sidebar contained **184 sessions** and was unnavigable —
it renders as one flat list and pushes the rest of the sidebar out of reach.

I did not pin 184 sessions and leave them pinned. Many of these had been unpinned earlier;
they reappeared in the Pinned list on their own. I cannot point at the moment it happened —
the IndexedDB history had been compacted and only covers the last day, and the app logs
that record unpin events go back one day as well — but the mechanism that would produce it
is present and measurable right now, see the section after next.

For scale, of the 184: 67 were last active in June, 107 in July, 10 in August. 170 of them
are also assigned to a sidebar folder (Boxy 94, CRM 49, and five smaller ones), so the
Pinned section was largely a duplicate of the folder contents.

There is no "Unpin all", no multi-select, and no bulk action on the section header. The
only supported way out is unpinning 184 sessions one at a time.

## Why this is a dead end rather than an annoyance

The pin state is mirrored across four layers, and each one is silently rebuilt from the
layer above it on the next launch:

1. **IndexedDB (master)** — origin `https://claude.ai`, record key
`store:pin-state:dframe-starred-code`, value is a JSON *string*:
`{"state":{"starredIds":[...]},"version":0,"updatedAt":}`
(a sibling record `store:pin-state:dframe-starred-cowork-remote` holds 1 id)
2. localStorage — `LSS-persisted.starred-local-code-sessions`, same origin
3. `claude_desktop_config.json` → `preferences.epitaxyPrefs`
4. per-session `claude-code-sessions///local_*.json` → `isStarred`

Clearing layers 2, 3 and 4 with the app fully closed appears to succeed and is then undone
at the next start. `main.log` shows, seconds after startup, one call per pinned session in
exactly the order of the old array:

```
LocalSessions.updateSession: sessionId=local_30f037c7-..., options={"isStarred":true}
... x184
```

Layer 1 cannot be reached with ordinary leveldb tooling, because Chromium uses its custom
`idb_cmp1` comparator for IndexedDB — the database refuses to open. The only route that
worked was enabling developer mode (`developer_settings.json` containing
`{"allowDevTools":true}` in the user data dir), pressing **Ctrl+Alt+I**, and rewriting the
record from the DevTools console. That succeeded and the lower layers picked up the new
value on the next restart — but it should not be the only way for a user to undo their own
pins.

## Unpinning never clears the underlying flag, so stale pins accumulate

`isStarred` in layer 4 is only ever set to `true`, never cleared. The startup reconcile
issues one `updateSession {"isStarred":true}` per pinned session and does nothing for the
rest. The result is a permanent record of every session that was ever pinned, and
`pinnedOrder` grows the same way.

This is measurable right now, minutes after I reduced the pin set to 12:

```
IndexedDB (master) 12
claude_desktop_config.json 12
pinnedOrder 192
session files isStarred 186 <-- 174 stale flags
```

So the data still holds a full list of everything ever pinned. Anything that rebuilds the
pinned set from layer 4 instead of from IndexedDB would resurrect all of them at once,
which matches what I experienced. `pinnedOrder` shows the same unbounded growth — it held
190 entries against 184 starred ids this morning, and holds 192 against 12 now.

## Suggested fixes

- Clear `isStarred` on unpin, and drop the id from `pinnedOrder`, so no stale pool builds
up and nothing can be resurrected from it.
- A bulk **"Unpin all"** action on the Pinned section header, plus multi-select. This alone
would have resolved the whole situation.
- Cap or collapse the Pinned list in the sidebar (show N, then "show all"), so it cannot
crowd out the folders and recent sessions below it.
- Keep `pinnedOrder` in sync with the starred set.
- If filing a session into a folder implies pinning it, decouple the two — that duplication
is what made the sidebar unusable.

## Reproducing on Windows

This is an MSIX install, so `%APPDATA%\Claude` exists only inside the app container. From
outside the container the real path is
`%LOCALAPPDATA%\Packages\Claude_\LocalCache\Roaming\Claude` — a script launched from
Explorer gets `ENOENT` on the `%APPDATA%` path, while the same path resolves fine for
processes started by the app.

### What Should Happen?

keep doing this fantasting job!

### Error Messages/Logs

```shell

```

### Steps to Reproduce

Environment: Claude Code 2.1.234, desktop shell 1.32885.1, Windows 11 Home 10.0.26200,
MSIX (Microsoft Store) install. ~500 local sessions, 184 of them pinned.

## A. The Pinned list grows without bound and cannot be cleared

1. Use the desktop app normally over a few months, pinning sessions from the left
sidebar as you go, and filing sessions into sidebar folders/groups.
(In my case this reached 184 pinned sessions: 67 last active in June, 107 in July,
10 in August. 170 of the 184 are also assigned to a folder, so the Pinned section
is largely a duplicate of the folder contents.)
2. Open the left sidebar and look at the "Pinned" section.
-> It renders all 184 entries as one flat list. Scrolling to anything is impractical
and the rest of the sidebar is pushed out of reach.
3. Right-click the "Pinned" section header, and right-click an individual pinned session.
-> There is no "Unpin all", no multi-select, no bulk action of any kind.
The only way out is unpinning 184 sessions one at a time.

Expected: a bulk "Unpin all" action on the Pinned header, and/or the Pinned list
capped/collapsed in the sidebar ("show N more"), and/or pins pruned automatically.

## B. The state is mirrored across four layers, so it cannot be fixed outside the app

This is what makes A a dead end rather than an annoyance. Each layer below is silently
rebuilt from the one above it on the next launch:

1. IndexedDB (master) - origin https://claude.ai, record key
`store:pin-state:dframe-starred-code`, value is a JSON *string*:
{"state":{"starredIds":[...184 ids...]},"version":0,"updatedAt":}
(sibling record `store:pin-state:dframe-starred-cowork-remote` holds 1 id)
2. localStorage - `LSS-persisted.starred-local-code-sessions` (same origin)
3. claude_desktop_config.json -> preferences.epitaxyPrefs
4. per-session claude-code-sessions///local_*.json -> "isStarred"

To reproduce the mirroring:

4. Fully quit the app (including the tray icon).
5. Edit layer 3 and layer 4 on disk: set `starred-local-code-sessions` to a short list in
claude_desktop_config.json, and set "isStarred": false in the corresponding
local_*.json session files.
6. Start the app.
-> Both files are back to their previous contents. main.log shows, seconds after
startup, one call per pinned session, in exactly the order of the old array:
LocalSessions.updateSession: sessionId=local_30f037c7-..., options={"isStarred":true}
... x184
7. Quit again and this time also clear layer 2 (the localStorage key above, writing
directly into Local Storage/leveldb), then start the app.
-> Still restored. Layer 2 is repopulated from layer 1.
8. Only editing layer 1 works, and layer 1 cannot be opened by ordinary leveldb tooling
because Chromium uses its custom `idb_cmp1` comparator for IndexedDB. The only route
left is enabling developer mode (developer_settings.json with {"allowDevTools":true}
in the user data dir), pressing Ctrl+Alt+I, and rewriting the record from the DevTools
console.

That last step is what I ended up doing, and it worked - but it should not be the only
way for a user to undo their own pins.

Note for anyone reproducing on Windows: this is an MSIX install, so %APPDATA%\Claude
only exists inside the app container. From outside the container the real path is
%LOCALAPPDATA%\Packages\Claude_\LocalCache\Roaming\Claude - a script run from
Explorer gets ENOENT on the %APPDATA% path.

### Claude Model

None

### Is this a regression?

Yes, this worked in a previous version

### Last Working Version

_No response_

### Claude Code Version

Claude Code: 2.1.234

### Platform

Anthropic API

### Operating System

Windows

### Terminal/Shell

PowerShell

### Additional Information

_No response_

Guida per i contributori

Nessuna guida per i contributori indicizzata per questo repository

Direzione di ricerca

Start by reproducing the startup reconciliation described in main.log, then inspect the IndexedDB record store:pin-state:dframe-starred-code alongside claude_desktop_config.json and the per-session local_*.json files. Done means unpinning removes stale isStarred and pinnedOrder entries, the Pinned section can be cleared or bounded in the sidebar, and the state remains correct after restart.

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

Valutazione

Stack tecnologico
node.js, python
Ambito
databases, desktop, frontend, operating-systems
Tipo di issue
Bug
Difficoltà
5/5
Tempo stimato
Più di una settimana
Stato di attività
Attiva
Chiarezza
Da chiarire
Idoneità per principianti
35/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.