[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

Status Fixed / completed
Reported on v2.1.214
Maintainer reply None cached
Activity 3 comments · opened Aug 10, 2026 · closed Aug 17, 2026

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:

  1. A structural scan of every session .jsonl under ~/.claude/projects found ZERO invalid JSON lines, no truncation, no torn writes. All real transcripts are healthy.
  2. The broken entries are 258–412 byte .jsonl files containing ONLY two metadata lines ({"type":"ai-title",...} and {"type":"agent-name",...}), no user/assistant/system entries. The conversation writer never wrote to them.
  3. Every ghost's session ID appears in ~/.claude/daemon.log as a background worker (bg spawned <id> (slash) / bg claimed-spare <id> (fleet)), timestamped to the second of the stub file's creation (excerpts below).
  4. Six of the eight stubs carry an aiTitle IDENTICAL 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.
  5. history.jsonl attributes 1–4 real typed prompts to each ghost session ID, which is what supplies the prompt that gets re-submitted after the failed resume.
  6. Opening a ghost renames it to <sessionId>.orphaned-<ms>-<hash>.jsonl and creates a fresh file under the same session ID whose first entry is a user message with parentUuid: 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 daemon bg 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:

  1. Use Claude Code normally for a few days (this machine: ~40 sessions over 25 days, native install on WSL2).
  2. Run: find ~/.claude/projects -name '*.jsonl' -size -2k -> title-only stubs accumulate (8 here between Jul 18 and Aug 9).
  3. Cross-reference each stub's 8-char session-ID prefix against bg spawned / bg claimed-spare lines in ~/.claude/daemon.log, creation times match to the second.
  4. Compare the stub's aiTitle with contemporaneous real sessions in the same project, 6 of 8 match a healthy large session exactly.
  5. 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).

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗