Session invisible in --continue/--resume due to 16KB buffer overflow in qj1
Bug Description
Sessions with large initial prompts (>16KB) become permanently invisible in claude --continue and --resume pickers, even though the session file is intact and the sessions-index.json has correct metadata.
Environment
- Claude Code version: 2.1.42
- OS: Ubuntu 24.04.4 LTS (Linux 6.17.0-14-generic)
- Platform: linux x86_64
Steps to Reproduce
- Start a session in a project directory
- Send a large initial prompt (>16KB) — e.g., via
EnterPlanModewhich passes a full implementation plan - Rename the session with
/rename(e.g., "My Important Session") - Continue working until the JSONL file grows significantly (>1MB)
- Exit with
/exit - Run
claude --continue— the session does not appear in the list - Run
claude --resume <session-id>— session not found
Root Cause Analysis
The function qj1 reads session JSONL files using a hardcoded 16KB buffer (eS$ = 16384):
// Reads FIRST 16KB → extracts firstPrompt
let D = await I.read(A, 0, eS$, 0);
let U = zj1(B); // firstPrompt from first 16KB
// Reads LAST 16KB → extracts customTitle
let X = Math.max(0, fileSize - eS$);
let J = (X === 0) ? B : await I.read(A, 0, eS$, X);
let F = xYH(J, "customTitle"); // customTitle from last 16KB
Then Tj1 filters sessions:
if (!L.firstPrompt && !L.customTitle) return null; // Session discarded
Problem 1 — firstPrompt not found: When the first user message exceeds 16KB, the buffer can only read the preceding file-history-snapshot (236B) and progress (624B) entries. The actual user message at byte 860 needs ~20KB and doesn't fit.
Example JSONL structure from affected session:
| Line | Type | Size | Cumulative |
|------|------|------|------------|
| 0 | file-history-snapshot | 236 B | 236 B |
| 1 | progress | 624 B | 860 B |
| 2 | user (first prompt) | 20,078 B | 20,938 B |
Buffer ends at 16,384 bytes — line 2 doesn't fit.
Problem 2 — customTitle not found: The custom_title entry (written by /rename) lands mid-file. In a 13.8MB session file, the last 16KB starts at byte 13,853,542 — the title at byte 374,093 is 13.4MB outside the buffer.
Critical design issue: Tj1 overwrites sessions-index.json metadata with values re-parsed from the JSONL:
let L = {...H, isLite: false, firstPrompt: A.firstPrompt, customTitle: A.customTitle, ...};
The sessions-index.json already had the correct customTitle and firstPrompt, but they get replaced with null from the failed buffer reads.
Workaround
Manually appending a custom-title JSON entry to the end of the .jsonl file places it within the last-16KB buffer:
echo '{"type":"custom-title","customTitle":"Session Name","sessionId":"<uuid>"}' >> ~/.claude/projects/<project>/<session-id>.jsonl
Suggested Fixes
| Priority | Fix |
|----------|-----|
| P1 | Don't overwrite sessions-index.json values with nulls from re-parse — trust the index when buffer reads fail |
| P2 | Increase eS$ from 16KB to at least 64KB |
| P3 | When writing custom_title via /rename, always append to end of JSONL (not just mid-stream) |
| P4 | Use sessions-index.json as authoritative source for the session picker, skip JSONL re-parsing when index data is available |
Affected Session Details
- Session file: 13.8 MB, 1,145 lines JSONL, 460 messages
- Trigger: Large prompt from
EnterPlanMode(implementation plan ~20KB) - Duration: ~95 minutes of active work, all lost from picker visibility
Impact
Any session started with a prompt exceeding 16KB that later grows beyond 32KB total will become invisible in --continue/--resume. This can happen naturally when using plan mode, pasting large code blocks, or receiving large context injections.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗