Custom session title overridden by AI-generated title; /resume forks duplicate custom titles onto new session IDs

Status Fixed / completed
Maintainer reply None cached
Activity 10 comments · opened Jul 16, 2026 · closed Aug 21, 2026

Environment

  • Claude Code v2.1.173, Linux (Ubuntu, kernel 6.17), zsh
  • Interactive CLI sessions

Summary

Two related problems make named sessions effectively unfindable in the /resume picker:

1. AI-generated title displayed instead of custom title.
After naming a session (e.g. "Feature A"), an ai-title entry is appended to the session .jsonl after the custom-title entry. The resume picker then shows the AI title, so searching for the custom name finds nothing. The user reasonably concludes the session was lost, although the file is intact on disk with the custom-title recorded.

Tail of the affected session file shows the ordering (names redacted):

{"type":"custom-title","customTitle":"\"Feature A\"","sessionId":"<uuid>"}
{"type":"ai-title","aiTitle":"<auto-generated summary title>","sessionId":"<uuid>"}
{"type":"agent-name","agentName":"\"Feature A\"","sessionId":"<uuid>"}

2. Custom titles bleed onto forked sessions.
Every /resume forks the conversation into a new session ID and re-stamps the old custom title onto the fork. In one project directory the same custom title appears on 3 different session files (name "B" on 3 sessions — including a brand-new session started fresh after /clear — name "C" on 3, name "D" on 2). The picker fills with duplicate names pointing at fork copies, burying the original named session.

Steps to reproduce

  1. Start a session, do some work, name it (rename/custom title).
  2. Keep working until an AI title is auto-generated, then exit.
  3. Run /resume — the session is listed under the AI title, not the custom name.
  4. Resume any named session, exit, and /resume again — the custom name now appears on multiple session entries (original + forks).

Expected

  • Custom title always wins over AI-generated title in the picker.
  • Forked/resumed sessions should not duplicate the custom name, or the picker should collapse fork chains.

Minor related issue

Quotes in the rename argument are stored literally: naming a session "Feature A" (quoted) stores the title including the quote characters.

View original on GitHub ↗

4 Comments

legion-the-geth · 1 month ago

Confirming part 2 (title duplication across /resume forks) on v2.1.212, Linux, with an extra data point: the picker interactions also leave title-only ghost session files behind.

After one working session yesterday (which involved the agent view / backgrounding), /resume now shows three entries with the same AI-generated title:

❯ Créer plugin Claude Code pour agents dev   · bg · 1.2MB
  Créer plugin Claude Code pour agents dev   · 254 bytes
  Créer plugin Claude Code pour agents dev   · HEAD · 778.7KB

Inspecting the .jsonl files in ~/.claude/projects/<project>/:

  • 1.2MB file — the live fork: full copy of the original session's active thread + the post-fork continuation. Confirmed by UUID comparison: all messages of the original file are present except one — an abandoned pre-edit user message (I had escaped and rephrased it), so the fork copies the active chain only.
  • 778.7KB file — the original session, frozen at the fork point. Orphaned on disk, same title.
  • 254-byte file — a pure ghost: created during /resume picker navigation, it contains only two metadata lines and zero messages:

``
{"type":"ai-title","aiTitle":"Créer plugin Claude Code pour agents dev","sessionId":"64d08439-..."}
{"type":"agent-name","agentName":"Créer plugin Claude Code pour agents dev","sessionId":"64d08439-..."}
``

A second ghost from the same evening (872 bytes) contains the same two lines repeated 4×, nothing else.

Side effect worth noting (relates to #68062): the fork broke in-flight background task tracking. A task-notification recorded before the fork pointed to /tmp/claude-1000/<project>/<old-session-id>/tasks/<task-id>.output; after the fork, TaskOutput on that task id returned No task found with ID: <task-id> — the task registry appears to be keyed by session id, so forks orphan running tasks and their UI status line silently disappears.

Happy to provide the (redacted) session files if useful.

littlemyx · 1 month ago

Confirming part 2 on macOS, v2.1.220 — with a version bisect that narrows it down, and one correction: the trigger is /new, not /resume.

What the transcripts show

When a new session is created, the previous session's title record is written as line 1 of the new session file, before any user message exists:

L1  {"type":"custom-title","customTitle":"<name belonging to an unrelated older session>","sessionId":"<new-uuid>"}
L2  {"type":"mode",...}
L7  <first real user message>

Because the session now already carries a title, no ai-title is ever generated for it — so the borrowed name is permanent. The app then re-stamps that same record into the transcript roughly once per turn: 17 identical records in a 400-line live file, 53 and 243 in older ones.

Version bisect

Grouping every session in ~/.claude/projects/** by the version field of its records, and counting sessions whose first title record precedes their first real user message:

| CLI version range | sessions created via /new | inherited a title |
|---|---|---|
| 2.1.156 → 2.1.202 (9 Jun – 8 Jul) | 21 | 0 |
| 2.1.204 → 2.1.220 (8 Jul – today) | 52 | 26 |

First affected versions are 2.1.204 / 2.1.207; nothing at all in the month before. (Zero counts inside individual later versions just mean the preceding session had not been given a title yet, so there was nothing to copy.)

The trigger is easy to misdiagnose

/new writes itself into the transcript under its legacy name: <command-name>/clear</command-name>. In my data 75 session files contain a /clear command record and exactly 1 contains /new, while my typed-prompt history shows /clear last used in April 2026 and /new used 142 times since. So the record name and the command name disagree, which makes this look like it originates from /resume or /clear.

Breakdown of 43 affected sessions by how the new session id was created — same mechanism in all cases:

  • /new — 21
  • background-job dispatch — 15 (the daemon spawns a separate CLI process, which resumes the parent transcript into a new session id)
  • /branch — ~5 (these at least get a (Branch) / (fork) suffix)
  • /compact — 1–2

Workaround

Quitting and relaunching claude instead of using /new is clean: of 205 sessions started by a plain launch, none inherited a name.

For anyone who wants their picker back: deleting every title record whose value equals the line-1 value, in sessions where a title record precedes the first user message, restores the fallback to each session's own first message. Across 29 such sessions that removed 847 records and cut my worst name collision from 17 sessions down to 6. Worth guarding two cases — recurring/scheduled tasks, where the repeated name is deliberate, and sessions whose own first message is a /compact continuation stub, where there is nothing better to fall back to.

spothapragada · 22 days ago

Confirming this pattern from a different angle — a long-lived recurring background job, not just a single /resume.

I have one named background job that's been backgrounded/resumed repeatedly over ~2.5 months (May → August). Every restart forks a new session ID, replays the original opening prompt, and carries the same job name forward — but there's no link back to the prior session at all. Result: 5+ separate session-list entries, all showing the identical name, with zero indication of which is current versus superseded, or that they're related at all.

This is the same root cause as this issue (fork-on-resume duplicates the title onto a new session ID) — it just compounds badly over time for anything that gets backgrounded/resumed repeatedly rather than once. Confirmed directly in the transcripts: the old session's last entry is literally "Backgrounding after the current tool finishes…", and the new session's first entries are ai-title/agent-name records carrying the identical name forward — so the harness clearly has the parent session ID at fork time, it's just not surfaced anywhere in the session list.

Related: #70373, #83971, #76493 — all describe the same fork-on-background/resume behavior from different angles (context loss, orphaning, task-list reset). Might be worth tracking as one root cause rather than four separate symptoms.

otakezo884 · 22 days ago

Adding an observation that may help narrow this down: we see the same fork-into-new-session-ID behavior (near-full transcript duplication) apparently triggered by interruption, not /resume or /clear.

Environment: Claude Code v2.1.222 (some files from v2.1.219/v2.1.221), Claude Desktop (Code tab), Windows 11 Pro build 26200.

  • One project directory accumulated 9 session .jsonl files for what was a single conversation. In 7 of the 8 split points, the last record of the old file was [Request interrupted by user], and a new file with a new session ID started 9 s – 2.5 min later (local-time pairs: 12:31:00→12:31:09, 12:42:02→12:42:13, 13:00:59→13:03:31, 14:03:11→14:03:25, 17:44:12→17:44:23, 22:32:10→22:32:59, 22:33:14→22:33:50).
  • Message-uuid overlap between paired files is 93–99% (e.g. 695/697 identical) — a near-full duplicate of the same conversation, not a new session.
  • Forked files share a fingerprint: first records are custom-titlemodequeue-operation, while a genuinely new session starts with queue-operation and has neither.
  • Independently reproduced in a second project directory on the same machine: 97.5% uuid overlap (1156/1186), newer file starts with custom-title.

Caveat: [Request interrupted by user] records can also be produced without user action (#78915, #70958, #84346), so this is correlation, not proven causation — but 7/7 anomalous splits immediately followed an interrupt record.

Possibly the same underlying resume/fork machinery as #82969, #76493, #80427, #77649. Practical impact of the duplication: transcript-based tooling double-counts sessions, and cleanupPeriodDays (default 30) deletes fragments per-file, punching holes in long conversations (#62959 / #63904).

Showing cached comments. Read the full discussion on GitHub ↗