[BUG] Desktop Code tab: /stats scan materializes entire multi-GB transcripts (incl. subagents/) → browser-process V8 heap OOM kills app; CLI handles same sessions fine

Status Fixed / completed
Reported on v2.1.170
Maintainer reply None cached
Activity 4 comments · opened Jun 11, 2026 · closed Jun 22, 2026

Preflight

  • Searched existing issues: the symptom is reported in #48261 (labeled invalid) and its closed duplicates (#55897, #49023), but no issue identifies this specific defect. This report isolates one concrete, code-level bug; it is intentionally narrower than #48261, whose thread demonstrably mixes at least two unrelated crashes (a commenter there confirmed a Windows variant that OOM-classifies at only ~442 MB working set — not heap exhaustion). Please don't dedup-close this into that thread.
  • Single bug report: yes.
  • Latest version: Claude Desktop 1.11847.5, bundled Claude Code SDK 2.1.170.

What's Wrong?

The Code tab's /stats transcript scan materializes every transcript file in full as parsed JS objects. The reader (a3r in the v1.11847.5 bundle) streams lines via readline but pushes every parsed entry into a single array before returning; the consumer then immediately filters to type === "user" || "assistant" and folds into small aggregates. For multi-GB transcripts (mine: one 2.4 GB / 366k-line main transcript plus ~3.5 GB of subagents/agent-*.jsonl from heavy subagent use), the parsed representation exceeds the ~4 GB V8 heap ceiling and the browser process aborts, killing the entire app — V8 heap OOM is not a catchable exception, so the scan's per-file try/catch cannot help. Files are also parsed in parallel batches, so peak memory is a whole batch, not one file.

Aggravating factors:

  • The scan reruns over all transcripts (~970 files here) on every Code-tab initialization.
  • The session-hydration path has a size mitigation (transcript is N bytes; tail-loading last 50 MB) — the stats scan has none.
  • Setting isArchived: true in the session store does not help; the scan walks ~/.claude/projects directly.
  • Because transcripts live in ~/.claude/projects, full app reinstalls (deleting ~/Library/Application Support/Claude) do not fix it — explaining several "reinstall didn't help" reports in #48261.
  • The CLI handles these exact sessions fine; only the desktop materializes full histories.

What Should Happen?

The scan should aggregate in O(1) memory per file. It already has map-reduce structure (parallel per-file parse → fold into date sets / counts / token sums); the reduce is just deferred until after full materialization. Moving the fold inside the existing readline loop — parse a line, keep only user/assistant entries projected to the few fields the stats read, accumulate, discard — yields identical output with no architectural change, since per-file accumulators already merge across the batch. Complementary hardening: cache per-file aggregates keyed on (path, size, mtime) so rescans touch only changed files, and/or extend the existing 50 MB tail-load cap to this path (with a log line, as hydration already does).

Error Messages/Logs

main.log immediately before every crash:

[warn] [CCD] Session f3ecf980-… transcript is 2572486670 bytes; tail-loading last 50 MB
[info] [CCD] /stats scanning 968/992 transcript files since 2026-03-09

Crashpad dump (browser process):

electron.v8-oom.is_heap_oom
electron.v8-oom.location = CALL_AND_RETRY_LAST
… Scavenge 3710.3 (3758.6) -> 3683.5 (3752.5) MB … allocation failure;

Steps to Reproduce

  1. Have a Claude Code CLI session whose transcript in ~/.claude/projects/<project>/ is multi-GB (long-running session with many subagents reproduces fastest; the subagents/ directory counts too).
  2. Launch Claude Desktop → app is stable on Chat/Cowork tabs.
  3. Open the Code tab → crash 5–15 s later. Switching to another tab after the Code tab has initialized does not prevent the crash (background scan).
  4. Workaround that confirms the cause: rename the oversized files so they no longer match the *.jsonl discovery filter (mv <session>.jsonl{,.bak}; mv <session>/subagents{,.bak}) → crash gone. Rename back → crash returns. Fully reversible.

Environment

  • Claude Desktop 1.11847.5 (bundled CC SDK 2.1.170), macOS 26.5.1 (Darwin 25.5.0), Apple Silicon
  • Model: Opus 4.8 (not model-dependent)
  • Regression: unknown — likely present since the stats scan shipped; triggers only once a transcript grows large enough.

Related

View original on GitHub ↗

4 Comments

JPBarringer · 4 months ago

Workaround until this is fixed in the app

If you are experiencing this problem and reading this: track down the large session log (and associated subagent logs) and rename them (add .bak, for instance) to avoid parsing them during load. Step by step:

  1. Quit Claude Desktop fully (check the menu-bar icon — it keeps running in the background).
  1. Find the oversized transcripts. In Terminal:

``bash
find ~/.claude/projects -name '*.jsonl' -size +100M -exec ls -lh {} \;
``

  1. Rename each large transcript so it no longer ends in .jsonl:

``bash
mv "<path-from-step-2>.jsonl" "<path-from-step-2>.jsonl.bak"
``

  1. Check for a folder with the same name as the session file (same long id, no .jsonl). If it contains a subagents folder, those transcripts are scanned too — rename that folder as well:

``bash
mv "<project-folder>/<session-id>/subagents" "<project-folder>/<session-id>/subagents.bak"
``

  1. Relaunch Claude Desktop.

This will break your ability to use those sessions in an active desktop session (and they won't appear in the CLI's --resume list either, since sessions are discovered the same way). But it will restore the desktop app to a usable state, and it is fully reversible once this is fixed — just rename the files back by removing .bak. Nothing is deleted.

github-actions[bot] · 4 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/50238
  2. https://github.com/anthropics/claude-code/issues/61815
  3. https://github.com/anthropics/claude-code/issues/66901

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

Necmttn · 3 months ago

/stats should operate over a bounded index, not parsed transcript arrays. Keep per-session counters updated at write or ingest time: message counts, token buckets when present, image/blob bytes, subagent versus main, first/last timestamp, and malformed-line count.

The UI path can stream rows and bucket subagents/ separately by default. Regression: a multi-GB transcript tree returns stats without materializing every JSONL entry in browser memory.

---

_Generated with ax._

github-actions[bot] · 1 month ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.