Skip to content

[BUG] A single metadata-only stub transcript makes /resume list empty for the ENTIRE project — 33 sessions hidden by one 416-byte file (2.1.210, macOS) #77898

Description

@purplemass

Summary

A single malformed "stub" transcript in ~/.claude/projects/<key>/ causes /resume to
show an empty list for that entire project. Not just the bad session — every
session in the folder disappears from the picker.

In my case one 416-byte file hid 33 healthy sessions, including multi-MB ones
from the same day. Deleting/moving that one file restored the full list immediately.

The transcripts are never damaged. claude --resume <id> restores them perfectly the
whole time — so this is purely a listing/enumeration bug, not data loss. But from the
user's side it is indistinguishable from "my history is gone", which is why I suspect it's
under-reported.

Environment

  • Claude Code: 2.1.210 (native install, ~/.local/bin/claude)
  • OS: macOS (Darwin 25.5.0)
  • Shell: zsh
  • Not VS Code — plain CLI.

The offending file

A transcript containing only metadata records — no cwd, no timestamp, no user or
assistant messages:

{"type":"ai-title","aiTitle":"connectivity-check","sessionId":"<uuid>"}
{"type":"agent-name","agentName":"connectivity-check","sessionId":"<uuid>"}
{"type":"ai-title","aiTitle":"connectivity-check","sessionId":"<uuid>"}
{"type":"agent-name","agentName":"connectivity-check","sessionId":"<uuid>"}

Mine was written by a daemon-backed background job (template: bg, backend: daemon)
that was spawned but never prompted — its ~/.claude/jobs/<id>/state.json still reads:

state: working
tempo: blocked
needs: send a prompt to start

So the session got named (ai-title/agent-name written) but never had a first turn,
leaving the transcript as pure metadata.

I found the same stub shape in 2 other projects on this machine, with ordinary work titles
("Review fixed code changes", "Change packaged executable naming") — so this is not
specific to background jobs. Any session that is titled and then abandoned before its
first turn appears to produce one.

Symptom

/resume in the affected project empty list (no sessions at all)
/resume in a project with no stub works fine
claude --resume <id> --fork-session -p "..." works — restores the "hidden" session perfectly
Transcripts on disk all intact, valid JSON, correct cwd

That third row is the key discriminator: resume-by-ID works while the listing is empty.
Storage is fine; the enumerator is what's failing.

Steps to reproduce

  1. Pick a project with several existing sessions; confirm /resume lists them.
  2. Write a stub transcript into that project's folder:
UUID=$(uuidgen)
cat > ~/.claude/projects/<encoded-cwd>/$UUID.jsonl <<EOF
{"type":"ai-title","aiTitle":"stub","sessionId":"$UUID"}
{"type":"agent-name","agentName":"stub","sessionId":"$UUID"}
EOF
  1. Run /resume in that project → list is empty; all previously-listed sessions gone.
  2. Remove the stub → list returns in full.

Likely cause

The picker appears to read each .jsonl for a sort key (timestamp/mtime) and a filter key
(cwd), and a record possessing neither seems to throw — aborting the whole directory scan
rather than skipping the entry. A malformed or unrecognised transcript should be skipped,
not fatal to its siblings.

Impact

Severity is amplified by how invisible the cause is:

Detection script

Finds stub transcripts across all projects:

python3 -c "
import json,glob,os
for f in glob.glob(os.path.expanduser('~/.claude/projects/*/*.jsonl')):
    has_cwd=has_user=False
    for line in open(f):
        line=line.strip()
        if not line: continue
        try: d=json.loads(line)
        except: continue
        if d.get('cwd'): has_cwd=True
        if d.get('type')=='user': has_user=True
    if not has_cwd and not has_user: print('STUB:', f)
"

Workaround

Move the stub out of the project folder (mv <stub>.jsonl /tmp/). They contain no
conversation, so nothing is lost. The picker recovers instantly — no restart needed.

Suggested fix

Make the enumerator defensive: wrap per-file parsing in a try/catch and skip files with no
usable timestamp/cwd rather than failing the directory. Optionally, don't persist an
ai-title/agent-name record until a session has its first real turn — that would stop
stubs being created at all.

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:corebugSomething isn't workingneeds-reproBug needs reproduction steps before we can investigateplatform:macosIssue specifically occurs on macOS

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions