[BUG] Daemon bg/spare sessions leave title-only .jsonl stubs in the resume picker; resuming errors, then starts a blank session that re-submits the last prompt
Preflight Checklist
- [x] I have searched existing issues 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?
Some entries in the resume picker behave as "corrupted sessions": selecting one shows a red error, and proceeding anyway lands in a blank session with no history that immediately re-submits the last prompt associated with that entry, which then gets answered with zero context. This has happened 8 times in ~3.5 weeks on one machine.
Forensics from the affected machine show these picker entries are ghost session files created by the background daemon, not corrupted transcripts:
- A structural scan of every session
.jsonlunder~/.claude/projectsfound ZERO invalid JSON lines, no truncation, no torn writes. All real transcripts are healthy. - The broken entries are 258–412 byte
.jsonlfiles containing ONLY two metadata lines ({"type":"ai-title",...}and{"type":"agent-name",...}), no user/assistant/system entries. The conversation writer never wrote to them. - Every ghost's session ID appears in
~/.claude/daemon.logas a background worker (bg spawned <id> (slash)/bg claimed-spare <id> (fleet)), timestamped to the second of the stub file's creation (excerpts below). - Six of the eight stubs carry an
aiTitleIDENTICAL to a large healthy session in the same project, created during or seconds after that session's activity (one stub was written 6 seconds after its twin's last transcript entry), so in the picker the ghost is indistinguishable from the real session. history.jsonlattributes 1–4 real typed prompts to each ghost session ID, which is what supplies the prompt that gets re-submitted after the failed resume.- Opening a ghost renames it to
<sessionId>.orphaned-<ms>-<hash>.jsonland creates a fresh file under the same session ID whose first entry is ausermessage withparentUuid: null, timestamped exactly at the orphan rename. A brand-new empty conversation firing the stale prompt. In some cases this orphan+restart was performed by a daemonbg claimed-spare ... (fleet)event rather than a user resume, so spare-reclaim recycles these IDs too.
Stubs were produced continuously by builds 2.1.214, 2.1.218, 2.1.220 and 2.1.226 (one created today under 2.1.226). Issue #66486 documents the same on-disk signature (ai-title-only stub) but was scoped to a v2.1.169 regression; this appears to be an ongoing daemon-side source of the same artifact, plus the resume-then-blank-restart behavior on top.
What Should Happen?
Background daemon sessions (spares, slash/fleet runners, title jobs) should either not materialize as project session files at all, or be excluded from the resume picker. Resuming an entry that contains no conversation should fail gracefully. It should not silently start a context-less session that re-submits a stale prompt as if the user had just typed it.
Error Messages/Logs
# Ghost stub contents (entire file, 258-412 bytes):
{"type":"ai-title","aiTitle":"...","sessionId":"<id>"}
{"type":"agent-name","agentName":"...","sessionId":"<id>"}
# ~/.claude/daemon.log lines matching each stub's file-creation time to the second:
[2026-08-09T03:15:00.181Z] [bg] bg spawned bff4385c (slash) <- stub mtime 03:15:01Z
[2026-08-09T08:42:50.993Z] [bg] bg claimed-spare a8063a0c (slash) <- stub mtime 08:42:51Z
[2026-08-09T23:37:53.456Z] [bg] bg claimed-spare 711ba4ab (slash) <- stub mtime 23:37:54Z
[2026-07-18T07:31:12.402Z] [bg] bg spawned e540e1c8 (slash) <- stub mtime 07:31:13Z
# Orphan rename produced when a ghost is opened:
a8063a0c-....orphaned-1786318302012-c81a12bf.jsonl
# ...and the fresh file created under the same session ID starts with a parentUuid:null
# user entry timestamped 2026-08-09T23:31:42.325Z. The orphan rename happened at
# 2026-08-09T23:31:42.017Z (same second).
# (Exact red error text from the resume UI not recorded, will add on next occurrence.)
Steps to Reproduce
Not deterministically reproducible from the outside (the trigger is daemon-internal), but reliably observable on an affected machine:
- Use Claude Code normally for a few days (this machine: ~40 sessions over 25 days, native install on WSL2).
- Run: find ~/.claude/projects -name '*.jsonl' -size -2k -> title-only stubs accumulate (8 here between Jul 18 and Aug 9).
- Cross-reference each stub's 8-char session-ID prefix against
bg spawned/bg claimed-sparelines in ~/.claude/daemon.log, creation times match to the second. - Compare the stub's
aiTitlewith contemporaneous real sessions in the same project, 6 of 8 match a healthy large session exactly. - In the resume picker, select one of these entries -> red error; proceed -> blank session re-submits the entry's last recorded prompt.
Claude Model
Not sure / Multiple models
Is this a regression?
I don't know
Last Working Version
Unknown: Stubs observed locally from 2.1.214 through 2.1.226; #66486 suggests the same artifact existed at 2.1.169
Claude Code Version
2.1.226 (Claude Code), native install
Platform
Anthropic API
Operating System
Ubuntu/Debian Linux
Terminal/Shell
WSL (Windows Subsystem for Linux)
Additional Information
- Session data lives on native WSL ext4 (not DrvFS/NTFS), 800+ GB free; no hooks configured; default settings.
- All real conversations remained intact and resumable under their own session IDs. The user-facing damage is trust in the picker, accidental context-less prompt re-submission, and the orphaned stubs left behind.
- Related: #66486 (same ai-title-only stub signature, scoped to v2.1.169 / WSL2+macOS).
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗