Archiving/deleting a session during `startSession` setup drops its store record but keeps the sidebar row — the row then spins forever and every subsequent archive/delete is a silent no-op

Status Fixed / completed
Reported on v2.1.229
Maintainer reply None cached
Activity 0 comments · opened Aug 15, 2026 · closed Aug 25, 2026

Archiving/deleting a session during startSession setup drops its store record but keeps the sidebar row — the row then spins forever and every subsequent archive/delete is a silent no-op

Environment

  • Claude Desktop 1.30096.5 (macOS, Darwin 27.0.0, Apple Silicon)
  • Claude Code 2.1.229 (the binary CCD spawns; CLI on PATH is 2.1.195)

Summary

There is a window during startSession setup in which a session exists in the sidebar but is not yet "live". If the user archives or deletes the session inside that window, CCD takes the applying deferred teardown instead of going live path. That path removes the session record from the store, but the sidebar row survives. The result is an orphan row that:

  • spins forever when opened, because the renderer keeps re-issuing a session load that logs not found after session load; skipping every ~30s indefinitely;
  • ignores every subsequent archive and delete — the IPC call is logged, but nothing follows it and the row never goes away;
  • is invisible to list_sessions even with include_archived: true, so it is not recoverable through the Archived list either.

The window is normally short (a few hundred ms), which is presumably why this is rarely hit. Anything that stretches startSession makes it trivially reproducible — in my case a stale auth token on a local MCP server stretched setup to a full 60s (see "How I widened the window" below), giving me a minute to click archive.

Steps to reproduce

  1. Arrange for startSession setup to be slow. Easiest: configure a local HTTP MCP server in ~/.claude.json whose endpoint returns 401 (a stale bearer token does it). MCP connect then burns its full 60s timeout inside setup.
  2. Start a new session. It sits on Starting session….
  3. While it is still on Starting session…, archive it (or press <kbd>Esc</kbd>, or delete it) from the sidebar's ⋮ menu.
  4. Wait for setup to finish.
  5. Click the row's ⋮ → Archive. Then ⋮ → Delete. Repeat.

Expected

The row disappears, or at minimum archive/delete report failure. A session whose record no longer exists should not remain listed.

Actual

The row stays in the sidebar indefinitely, opens to a permanent spinner, and archive/delete do nothing at all — no error, no toast, no state change.

Log evidence

Two sessions in one evening, local_181c9e6d… and local_ba95a411… (~/Library/Logs/Claude/main.log).

Session A — teardown during setup, then six ignored user actions:

02:39:47 [info] Session local_181c9e6d… was deleted during startSession setup; applying deferred teardown instead of going live
02:39:47 [info] Archived session local_181c9e6d…
02:39:47 [warn] Cannot update session: local_181c9e6d… not found
02:39:49 [info] LocalSessions.archive: sessionId=local_181c9e6d…      <- user click
02:40:02 [info] LocalSessions.delete:  sessionId=local_181c9e6d…      <- user click
02:40:05 [info] LocalSessions.archive: sessionId=local_181c9e6d…      <- user click
02:40:06 [info] LocalSessions.archive: sessionId=local_181c9e6d…      <- user click
02:52:18 [info] LocalSessions.delete:  sessionId=local_181c9e6d…      <- user click
02:53:28 [info] LocalSessions.archive: sessionId=local_181c9e6d…      <- user click

Six invocations after the initial teardown, and not one of them is followed by a result line of any kind.

Meanwhile, from 02:39 to 02:53 — fourteen minutes — the renderer keeps trying to load the session it no longer has, roughly every 30s to 2min. This is what the endless spinner is:

02:40:58 [warn] [LocalSessionManager] [replaceRemoteMcpServers] Session "local_181c9e6d…" not found after session load; skipping
02:41:18 [warn] … same …
02:41:52 [warn] … same …
02:42:24 [warn] … same …
02:45:02 [warn] … same …
02:47:11 [warn] … same …
02:50:43 [warn] … same …
02:51:05 [warn] … same …
02:51:30 [warn] … same …
02:52:12 [warn] … same …
02:53:26 [warn] … same …

Messages typed into an orphan row are rejected, and the rejection is swallowed. From the renderer log (~/Library/Logs/Claude/claude.ai-web.log), at the moment of teardown:

02:39:47 [error] [REACT_QUERY_CLIENT] QueryClient error:
  Error invoking remote method '…claude.web_$_LocalSessions_$_sendMessage':
  Error: Session "local_181c9e6d…" not found [ccdErrorCategory:unknown]

The composer had accepted the message and kept showing it as pending; nothing surfaced this failure to the user. The message was never persisted — the orphan has no .jsonl under ~/.claude/projects/, so it is simply gone.

Session B — same shape, plus delete visibly falling through to the archive branch:

02:51:49 [info] LocalSessions.archive: sessionId=local_ba95a411…
02:51:51 [info] Session local_ba95a411… was archived during startSession setup; applying deferred teardown instead of going live
02:51:51 [info] Archived session local_ba95a411…
02:54:45 [info] LocalSessions.delete:  sessionId=local_ba95a411…
02:54:45 [info] Archived session local_ba95a411…                       <- delete logged an archive
02:56:19 [info] LocalSessions.delete:  sessionId=local_ba95a411…       <- no result line

Control: a normally-archived session behaves correctly

A third session that survived setup and was archived afterwards works exactly as expected:

02:53:24 [info] LocalSessions.archive: sessionId=local_38e66890…
02:53:24 [info] Archived session local_38e66890…

and it is present in the session listing:

{ "sessionId": "local_38e66890-…", "title": "用户消息显示延迟问题",
  "isArchived": true, "isRunning": false }

The two orphans are absent from that same listing with include_archived: true, and have no .jsonl under ~/.claude/projects/. So the record is genuinely gone while the row remains — the sidebar and the store have diverged.

Minor, same area

An orphaned row that has already been archived still renders a live, ticking elapsed timer (1m 21s and counting) above the "This session is archived. Unarchive it to continue the conversation." banner. Cosmetic, but it is part of why these rows read as "still working" rather than "broken".

Suggested fix

Either keep the record (archived) when teardown is deferred, so the row stays actionable; or have the deferred-teardown path remove the sidebar row along with the record. Additionally, LocalSessions.archive / LocalSessions.delete should surface a not-found error to the UI rather than resolving silently, so a row can never look actionable while being unactionable — sendMessage already produces exactly such an error, it just never reaches the user.

How I widened the window (context, not the bug)

Session start blocks serially on MCP connect. A local MCP server of mine was returning 401 instantly (stale bearer token in ~/.claude.json), yet CCD still burned the full 60s timeout:

[CCD start-timing] local_38e66890… preflight=4ms worktree=1ms mcp=60024ms query=6ms
  enqueue=1ms init=2120ms first_assistant=17077ms |
  ccd_overhead=60035ms total_to_init=62156ms total_to_assistant=79233ms mcp_count=12

Normal runs on this machine are mcp=400–800ms, total_to_init≈1.8s. Note the CLI fails the same server fast — claude mcp list reports ✘ Failed to connect in 1.6s total — so the 60s is specific to this path.

That timing behaviour is arguably its own issue (a deterministic 4xx should not cost 60s, and the UI never says which server it is waiting on), but the orphan-row defect above is independent of it: the slow start only widens an existing race window from milliseconds to a minute.

View original on GitHub ↗