--resume picker labels sessions with real prompts as "(session)" — title lookup only reads 64KB head/tail

Status Open
Reported on v2.1.211
Maintainer reply None cached
Activity 1 comment · opened Jul 16, 2026

Summary

The --resume picker labels sessions (session) — the fallback meant for
empty sessions — even when the transcript contains plenty of real prompts. The
label only reflects whether a title-bearing line happened to land inside the
64 KB the picker reads, not whether the session has content.

Same root cause likely explains #75602 (custom titles on disk but not shown,
"visible earlier, then disappeared without the underlying data changing").

Environment

  • Version: 2.1.211
  • Platform: linux, xterm-256color

Mechanism

The picker's lite loader (XId) derives the row label from a windowed read.
Npl reads zL = 65536 bytes from offset 0 (head) and 64 KB from size - zL
(tail). The middle of the transcript is never read. All three label sources come
only from those two windows:

let{head:n,tail:o}=await Npl(e,t,r);
let m = LT(o,"lastPrompt") || aBy(n) || yId(n,"content",200) || yId(n,"text",200) || "";
let g = LT(o,"customTitle") ?? LT(n,"customTitle");
let y = LT(o,"aiTitle")     ?? LT(n,"aiTitle");
if(!s.firstPrompt && !s.customTitle && !s.aiTitle) s.firstPrompt="(session)";

A session is labeled (session) when it has no customTitle, no in-window
aiTitle, and more than 64 KB of trailing content after its last
last-prompt line
(large tool results, file-history-delta runs). The
session is not empty — the picker just never read far enough to find its title.

Repro

A transcript where:

  • the first 64 KB has no "type":"user" line and no "content":" / "text":"

(e.g. mode, permission-mode, a large file-history-snapshot),

  • real user + last-prompt lines sit in the middle,
  • the last 64 KB has no lastPrompt / aiTitle / customTitle

(e.g. a long file-history-delta run).

Then claude --resume in that project.

A/B on one 141 KB file containing 4 genuine user prompts:

| file | picker row |
|---|---|
| as above | (session) |
| + one last-prompt line appended into the tail (~200 bytes, prompts untouched) | renders that prompt |

Only in-window-ness changed. The prompts never moved.

Expected vs actual

  • Expected: the row shows the session's prompt (or its title).
  • Actual: (session), indistinguishable from a genuinely empty session.

Why it's worth fixing

Cosmetic — sessionId is untouched and the row resumes correctly — but real
sessions look empty and unidentifiable, and it misleads anyone debugging it: it
presents as transcript corruption or a broken SessionEnd hook. It cost me a
couple of sessions chasing lifecycle theories before I read XId.

The margin is thinner than it looks. Across my transcripts the last
title-bearing line normally sits 113–1462 bytes from EOF (these lines are
rewritten often), but I have one at EOF-34483 — over half the tail window
consumed. A single large trailing tool result pushes it out.

Possible fix

When both windows come up empty, fall back to a bounded scan for a
title-bearing line before declaring the session untitled — rather than
labeling a session with content as (session).

View original on GitHub ↗

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