Desktop: every session switch spawns a throwaway Claude Code session — 950 unused sessions in 4 weeks, inflating claude_code.session.count and littering ~/.claude/projects

Status Open
Reported on v2.1.247
Maintainer reply None cached
Activity 1 comment · opened Aug 30, 2026

Summary

On the macOS desktop app, switching focus between parallel sessions pre-warms the target via WarmLifecycle, and each warm spawns a fresh Claude Code CLI session. That warmed session is then never used: when the conversation actually resumes, the app maps the local session to its pre-existing CLI session. The spawned session therefore starts, fires SessionStart, writes its per-session directories, and is discarded without ever receiving a message.

Over ~4 weeks this machine accumulated 950 sessions holding no conversation against 57 that hold one — 94% waste.

Two consequences I could not find covered in the existing WarmLifecycle reports (#76268, #85104, #82023 all frame the problem as memory / process accumulation):

  1. claude_code.session.count is incremented for each of these. OTel session counts end up dominated by sessions that never made an API call, so session counts climb while token, cost and active_time metrics stay flat.
  2. ~/.claude/session-env/<uuid>/ and ~/.claude/projects/<project>/<uuid>/ are created per spawn and are never pruned.

Environment

  • Claude Desktop 1.40609.0 (macOS)
  • Bundled Claude Code 2.1.247
  • macOS 26.6.2 (25G83), Apple Silicon (Mac Studio)
  • 3 plugins; 21 MCP servers / 213 tools per session; one SessionStart hook
  • Usage pattern: 2–4 Code sessions open in parallel, switched between constantly

Mechanism

Every warm block in ~/Library/Logs/Claude/main.log has the same shape:

[CCD] LocalSessions.setFocusedSession: sessionId=null
[WarmLifecycle:preview] Warming up session local_<id>
[oauth-v2] using cached token for orgId=<redacted>
LocalSessions.checkTrust: cwd=<path>
[CCD] LocalSessions.setFocusedSession: sessionId=local_<id>
[CCD] LocalSessions.replaceEnabledMcpTools: sessionId=local_<id>, toolCount=213
[CCD] LocalSessions.replaceRemoteMcpServers: sessionId=local_<id>, serverCount=8
LocalSessions.startShellPty: sessionId=local_<id>
handoff: publishing session_<id>

The spawned CLI session's UUID is not written to the app log — the app logs a CLI session id only when it maps one to a conversation — but each spawn is observable on disk as a new ~/.claude/session-env/<uuid>/ and ~/.claude/projects/<project>/<uuid>/.

Measurements — one busy day (2026-08-17)

| Signal | Count |
|---|---|
| setFocusedSession: sessionId=null | 143 |
| … switching to a different session | 137 |
| … returning to the same session | 2 |
| LocalSessions.startShellPty | 137 |
| CLI sessions created on disk | 116 |
| CLI sessions ever mapped to a conversation | 4 |
| CLI sessions never used | 113 |

106 of the 116 on-disk session creations (91%) align within ±2 s with a Warming up session / startShellPty pair.

Mapping internal session local_<id> to CLI session <uuid> appears 180 times that day but references only 4 distinct CLI sessions, which is what confirms the warmed sessions are discarded rather than attached.

It scales with switch count, not with work done

| Date | Parallel sessions | Messages sent | Focus switches | Sessions spawned |
|---|---|---|---|---|
| 08-17 | 3 | 118 | 143 | 137 |
| 08-20 | 3 | 76 | 141 | 136 |
| 08-27 | 4 | 60 | 126 | 115 |
| 08-26 | 4 | 28 | 79 | 60 |
| 08-25 | 3 | 100 | 33 | 29 |
| 08-23 | 1 | 11 | 2 | 1 |
| 08-15 | 1 | 5 | 0 | 0 |

Compare 08-25 (100 messages → 29 spawns) with 08-26 (28 messages → 60 spawns). The driver is the number of focus switches, which scales with how many sessions are kept open, not with how much work is done. Single-session days produce almost none — which is probably why this has not been reported before.

Disk and inode cost

Over ~4 weeks on this machine:

  • ~/.claude/session-env/: 966 directories, 965 files
  • 950 unused session directories under ~/.claude/projects/, containing 1,875 directories and 976 files
  • ~4,800 inodes in total

Bytes are modest (33.5 MB) and are dominated by a single artifact: SessionStart hook stdout is captured to <uuid>/tool-results/hook-<id>-stdout.txt on every spawn, including spawns that are discarded immediately. My hook emits ~37 KB, so ~37 KB is written per discarded session. An installation without a large SessionStart hook would see the directory and inode churn without the megabytes.

Nothing prunes any of this. cleanupPeriodDays targets transcripts, and these directories have no transcript.

Expected

Any one of the following would resolve it:

  • reuse the warmed session when the user switches to it, so the warm is not wasted; or
  • do not spawn a CLI session for a warm until the session is actually attached; or
  • at minimum, tear down and delete the per-session artifacts of a warm that is never attached, and do not count it in claude_code.session.count.

Related

#76268, #85104 and #82023 concern the same subsystem but report memory and process accumulation. This report is about the session identity churn — metric counts and on-disk artifacts — which those issues do not cover.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗