[BUG] Session titles revert to last-prompt text after session close, overriding custom-title entries

Status Open
Reported on v2.1.62
Maintainer reply None cached
Activity 11 comments · opened Mar 8, 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?

These chat sessions that worked with the 'first line as title', were critical to me correlating work in Claude Code chat sessions <-> kanbn task manager used for the project.

So now I'm having to have Claude try to rename the things that it is automatically renaming, that I already had a solid title for in the very first line.

Chat sessions get renamed seemingly at random, and are then mostly un-searchable as the 'session title' has been auto-edited to some arbitrarily-chosen text 'somewhere in the chat session' by (Claude Code, VScode ???).

After a session ends, the Claude Code VS Code extension writes a last-prompt entry to the session's JSONL file. This entry becomes the final entry in the file, and the sidebar then displays the lastPrompt text (the user's final message) as the session title — overriding any custom-title entry that was written earlier in the file.

Previously, the sidebar used the first user message as the session title by default. Now, closed sessions display their last message text instead. This makes sessions unrecognisable in the sidebar, especially for users who rely on writing a descriptive title as the first line of their first message (e.g. "Task: Task 186.1 - LLM Scoring Heatmap Load Performance — Backend Query Optimisation and Caching" to identify sessions and pick up e.g. kanbn Task cards to continue the work.

What Should Happen?

The session sidebar should display the custom-title entry as the title regardless of whether a last-prompt entry was appended afterward.

Previously, sessions were titled by their first user message line, and the title persisted correctly across VS Code restarts and session close/reopen cycles. The last-prompt entry should not override a custom-title entry when displaying session names in the sidebar.

Manual renames (via the edit icon in the sidebar) do work correctly because VS Code appends the new custom-title after last-prompt, making it the final entry. This confirms the sidebar is using "last entry wins" logic rather than "custom-title wins" logic. The fix would be to prioritise any custom-title entry over last-prompt for display purposes.

But again, this adds a lot of friction, when 'first line is title' worked fine, with no more overhead than pasting in the task card title that I was working on.

Error Messages/Logs

No error messages. The behaviour is silent — sessions simply display the wrong title (last message text instead of the session's custom-title).

Example JSONL file tail showing the problem:
  ...conversation entries...
  {"type":"custom-title","sessionId":"25e59705-...","customTitle":"Find the chat session where we added a custom hook or script..."}
  {"type":"last-prompt","lastPrompt":"Good, will test by restarting / reloading window shortly...","sessionId":"25e59705-..."}

Sidebar displays: "Good, will test by restarting / reloading window shortly..."
Expected display: "Find the chat session where we added a custom hook or script..."

Steps to Reproduce

  1. Open Claude Code in VS Code and start a new session.
  2. Type a descriptive first message (e.g. "Task: Task 149 - My Task Name").
  3. Have a multi-turn conversation.
  4. Close the session (close the tab or open a new session, or reopen VScode).
  5. Observe the closed session in the sidebar — it now displays the text of your last message, not your first message or any previously set custom-title.
  6. Restart VS Code — the wrong title persists across restarts.

Root cause (confirmed by inspection of JSONL files): the extension appends a {"type":"last-prompt","lastPrompt":"..."} entry when a session closes. The sidebar then displays lastPrompt because it is the final entry in the file, taking precedence over earlier custom-title entries.

Manual renames via the sidebar edit icon do persist correctly, because VS Code appends the new custom-title after last-prompt — confirming the issue is position-dependent, not a reading failure.

Claude Model

Sonnet (default)

Is this a regression?

Yes, this worked in a previous version

Last Working Version

Unknown — noticed after a recent extension update. Last known working behaviour: sessions displayed the first line of the first user message as the title by default, and this persisted correctly after session close and VS Code restart.

Claude Code Version

2.1.62 (Claude Code)

Platform

Anthropic API

Operating System

Ubuntu/Debian Linux

Terminal/Shell

Other

Additional Information

VS Code version: 1.109.5 (x64)

The workaround we implemented locally:

  • A systemd user timer runs find-session.py --fix-last-prompt every 2 minutes
  • It detects sessions where last-prompt is the final JSONL entry and no custom-title follows it
  • Appends a compact custom-title (from the session's first user message line) after last-prompt
  • Restores the original file mtime afterward to preserve sidebar sort order

This workaround works, but it means sessions show the wrong title for up to 2 minutes after closing, and requires external tooling to compensate for what should be (or was, now isn't, but could be again...) built-in behaviour.

Impact: users who rely on descriptive first-line titles to identify sessions (a natural workflow given the sidebar shows session titles) will find all their closed sessions display meaningless last-message text instead. With 500+ sessions, this makes the session list unusable without the workaround.

System Details Report

---

Report details

  • Date generated: 2026-03-08 14:52:19

Hardware Information:

  • Hardware Model: Gigabyte Technology Co., Ltd. X870E AORUS ELITE WIFI7
  • Memory: 128.0 GiB
  • Processor: AMD Ryzen™ 9 7900X × 24
  • Graphics: AMD Radeon™ Pro WX 3200 Series
  • Graphics 1: AMD Radeon™ Pro WX 3200 Series
  • Disk Capacity: 13.0 TB

Software Information:

  • Firmware Version: FA6d
  • OS Name: Ubuntu 24.04.4 LTS
  • OS Build: (null)
  • OS Type: 64-bit
  • GNOME Version: 46
  • Windowing System: Wayland
  • Kernel Version: Linux 6.17.0-14-generic

View original on GitHub ↗

11 Comments

github-actions[bot] · 5 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/25090
  2. https://github.com/anthropics/claude-code/issues/31270
  3. https://github.com/anthropics/claude-code/issues/31394

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

interconnectedMe · 5 months ago

Not a duplicate of - https://github.com/anthropics/claude-code/issues/31394 -
Possible partial overlap with 31394, but that is on MacOS, and also does not address the issue in mine of 'user does not have control over chat session names' in general.

Not a duplicate of https://github.com/anthropics/claude-code/issues/31270 -
that issue's 'The names revert to the truncated first prompt' - is exactly the reverse of what I have, in some way, I think?

Possibly a partial duplicate of https://github.com/anthropics/claude-code/issues/25090 -
But, that issue is related to Windows, and also does not address the issue in mine of 'user does not have control over chat session names' in general.

interconnectedMe · 5 months ago

I had Claude Code kick off some research in NotebookLM and document what it could trace through the 'duplicate trail'. I'm not sure if this is helpful, but as ever, the AI sounds confident about it ;-)

Notebook - https://notebooklm.google.com/notebook/98a2abca-848f-4838-b32b-e1586eadca57
Mindmap -

<img width="3840" height="5737" alt="Image" src="https://github.com/user-attachments/assets/f45b3d44-75cd-4c75-8307-ffdee1f93773" />

Technical Briefing: Session Renaming and Persistence Failures (GitHub Issues 23151–32150)

  1. Canonical Tracking Identification

To minimize notification noise and consolidate engineering triage, all internal and community tracking must be directed to the following designated issues:

  • Primary Technical Canonical Issue: Issue #27202. This serves as the authoritative root-cause analysis for the JSONL scanner logic failure and title-to-UUID routing errors.
  • Active Community Tracking Issue: Issue #31394. This is the designated collector for production telemetry on the latest release (v2.1.70), specifically capturing macOS Darwin 25.3.0 environment data.

Recommendation: All users should upvote and watch Issue #31394 to provide the team with consistent telemetry for current-version performance and to receive status updates.

--------------------------------------------------------------------------------

  1. Executive Problem Statement and Root Cause Analysis

The persistent failure of the /rename command and the subsequent loss of session titles represent a critical State Synchronization Failure. This originated during the Architectural Pivot where the system transitioned from the centralized sessions-index.json to a decentralized JSONL-stream scanning model.

Technical synthesis identifies a three-pronged failure in the current architecture:

  • Scanner Logic Error (The "Smoking Gun"): In v2.1.49, the system transitioned to a JSONL scanner that performs a readdirSync and parse on transcript files. Evidence from #27202 (19KB file size mismatch) confirms that the scanner finds custom-title entries but ignores the sessionId field. It incorrectly applies the title to whatever file is currently being read, leading to titles being attributed to the wrong sessions or lost entirely.
  • LIFO "Last Entry Wins" Conflict: There is a fundamental race condition in the VS Code extension's lifecycle. While /rename triggers a write during the session, the extension appends a last-prompt entry on close. The sidebar UI employs a Last-In-First-Out (LIFO) display logic; because the last-prompt is the final entry in the JSONL stream, it overrides any previously written custom-title (#32150).
  • Index Cache Mismatch: To facilitate fast startup, the system still maintains sessions-index.json as a lightweight cache. However, this index is suffering from schema drift. It lacks the displayName field required for name-based lookups (#26249) and the customTitle field used by the UI (#31270). Consequently, events like "Reload Window" force the UI to revert to the cached firstPrompt because the full JSONL parse is deferred.

--------------------------------------------------------------------------------

  1. The 12-Issue Duplicate Chain Matrix

The following matrix tracks the fragmented history of this regression across environments and versions.

Issue # Status Environment Primary Symptom Linked Issue(s)
#27202 Open (Stale) Linux (Fedora) Scanner ignores sessionId; titles route to wrong files. #24065, #27195
#31394 Open macOS (Darwin 25.3.0) Rename fails to persist in /resume list (v2.1.70). -
#26249 Duplicate macOS (Sequoia) Cannot resume by name; displayName missing from index. #26134
#23151 Duplicate macOS Renamed sessions invisible in picker (missing summary). #23061
#26134 Duplicate Windows 11 /rename visual success but 0 persistence in index. #26249
#24729 Open Windows 11 Indexing stops; summaries empty (since Feb 2, 2026). #23151
#25090 Open Windows 11 Name persists for one resume, vanishes after second exit. #24065
#31270 Open VS Code (Win) Custom names reset to firstPrompt after window reload. #32150
#29922 Open Linux Regression: Name lost after context compaction triggers. #26121
#32150 Open VS Code (Linux) Sidebar titles revert to last-prompt text on close. #31270
#25905 Duplicate macOS CLI resume fails; index entry absent after rename. #26249
#24065 Open (Stale) macOS Custom title not saved to index; resume list ignores it. #27202

--------------------------------------------------------------------------------

  1. Version Timeline: Evolution, Fixes, and Regressions
  • v2.1.29–v2.1.38 (Early 2026): Transition period for sessions-index.json.
  • Feb 2, 2026: Summary generation failures begin. Renamed sessions without summaries become invisible in the /resume picker (#23151, #24729).
  • v2.1.39–v2.1.42: Introduction of the "second exit" bug. The auto-titling logic runs on session close and fails to check for existing custom titles, overwriting user data (#25090).
  • v2.1.47: Patch released to address context compaction name loss.
  • v2.1.49: Introduction of the JSONL scanner. Modernization attempt introduces the logic error where the scanner ignores the sessionId within the transcript (#27202).
  • v2.1.62–v2.1.63: Regression Test Failure. The fix from v2.1.47 regresses in v2.1.63, with names again lost during compaction (#29922). VS Code sidebar begins prioritizing last-prompt (LIFO logic) over custom-title (#32150).
  • v2.1.69–v2.1.70: Current production state. Persistence is failed on macOS Darwin 25.3.0, and "Reload Window" resets all titles to the first prompt cache (#31270, #31394).

--------------------------------------------------------------------------------

  1. Categorization of Persistence Failures
  • Architectural (The Scanner/Index): Core failures in how Claude Code routes and caches identity.
  • Evidence: Users have bypassed the scanner using cc-titles.json via external wrappers (#27202).
  • Suggested Fix: Update renameSession() to perform an atomic write to the sessions-index.json cache during the transaction to prevent reload resets (#31270).
  • Lifecycle (Compaction/Exit): State loss during active session management.
  • Evidence: Auto-titling triggers on second exit 100% of the time in some environments, overwriting customTitle metadata (#25090).
  • UI/IDE Specific (VS Code Sidebar/Reload): LIFO display errors in the extension.
  • Evidence: Use of the find-session.py workaround. This script must manually re-patch JSONL files and—crucially—restore the original fileMtime to prevent breaking the sidebar's chronological sort order (#32150).

--------------------------------------------------------------------------------

  1. Current Status and User Guidance

As of v2.1.70, the /rename feature is Effectively Cosmetic and Unreliable. Titles may appear to save during a session but are frequently lost due to compaction, auto-titling on exit, or IDE reloads.

Known Manual Workarounds:

  1. Index Summary Patching: Manually editing sessions-index.json to copy the customTitle value into the summary or displayName fields.
  2. JSONL Transaction Forcing: Manually appending a custom-title entry to the end of a session's JSONL file after the session has closed to ensure it is the final entry (defeating LIFO overrides).
  3. External Scripting: Utilizing tools like find-session.py to periodically re-apply titles while preserving fileMtime.

Closing Directive: All users on the latest version (v2.1.70) must move engagement to Issue #31394. This issue is the primary focus for engineers investigating the current Darwin 25.3.0 environment failures.

interconnectedMe · 5 months ago

Claude Code has at least for me, on my linux box, figured it out.
My chat sessions titles are now stable.

Root Cause Breakdown

Layer 1: Index Schema Missing customTitle

sessions-index.json schema fields: sessionId, fullPath, fileMtime, firstPrompt, summary, messageCount, created, modified, gitBranch, projectPath, isSidechain. No customTitle or displayName field. The /rename command writes to JSONL only; the picker reads from the index only.

Layer 2: VS Code "Last Entry Wins"

The VS Code extension displays session titles using priority: last entry in JSONL file wins. On session close, it appends {"type":"last-prompt","lastPrompt":"..."}. Since this appears after custom-title, the last message text becomes the displayed title.

Layer 3: Auto-Titling Overwrites

Context compaction and auto-titling routines don't check for existing user-defined titles. They regenerate summaries that stomp custom names.

Layer 4: Scanner Ignores sessionId

Since v2.1.49, the JSONL scanner applies custom-title to the file it's found in, not the session specified in the sessionId field. Renaming after /resume titles the wrong session entirely.

Version Timeline

| Version | Status | Notes |
|---------|--------|-------|
| v2.1.29 | Broken | #23151: customTitle not synced to summary |
| v2.1.31 | Broken | #24729: session index stops updating entirely |
| v2.1.37–42 | Broken | #24065, #26134, #26249: /rename not indexed |
| v2.1.47 | Partial fix | #23610 fixed, but rename still not indexed |
| v2.1.49 | New bug | JSONL scanner ignores sessionId → wrong session titled |
| v2.1.53 | Fixed briefly | Custom names appeared in Ctrl+E picker |
| v2.1.55 | Regressed | Fix reverted (possibly side-effect of EINVAL fix) |
| v2.1.62–69 | Broken | VS Code: last-prompt overrides custom-title |
| v2.1.70 | Broken | Current — all symptoms persist |

Our Local Workarounds

  1. auto-title-session.sh hook — UserPromptSubmit hook that writes first-line-as-title to JSONL
  2. find-session.py --retitle — Retroactive batch retitling of untitled sessions
  3. find-session.py --fix-last-prompt — Detects sessions where last-prompt is final entry, appends custom-title after it (runs on systemd timer every 2 min)

Primary Sources

| # | Issue | State | Platform | Key Contribution |
|---|-------|-------|----------|-----------------|
| 1 | #32150 | OPEN | Linux/VS Code | last-prompt overrides custom-title (yours) |
| 2 | #25090 | OPEN | Windows/Cross | Most active tracker, version-by-version log |
| 3 | #24729 | OPEN | Windows | Infrastructure: session index broken since v2.1.31 |
| 4 | #31270 | OPEN | Windows/VS Code | sessions-index.json missing customTitle field |
| 5 | #29922 | OPEN | Linux | Name lost after compaction (regression label) |
| 6 | #27202 | OPEN | Cross | Scanner ignores sessionId — titles wrong session |
| 7 | #24065 | OPEN | macOS | Early canonical report, frequently referenced |
| 8 | #31394 | OPEN | macOS | Latest report, v2.1.70 |
| 9 | #26134 | OPEN | Windows | Duplicate label but has useful confirmations |
| 10 | #23151 | CLOSED | macOS | First to identify customTitle vs summary gap |
| 11 | #26249 | CLOSED | Cross | Root cause analysis: rename not indexed |
| 12 | #25905 | CLOSED | macOS | Rename makes sessions unresumable |

NotebookLM Analysis

NotebookLM identified four architectural failure layers: cache/index desync, auto-titler overwrite, last-entry-wins UI logic, and scanner sessionId ignore. The brief fix in v2.1.53 (regressed in v2.1.55) proves the team knows where the fix is — it just didn't stick, likely reverted as a side-effect of the Windows EINVAL fix in the same release.

The suggested fix is straightforward: add customTitle to the sessions-index.json schema, have /rename write to both JSONL and index, and have the UI prioritise custom-title entries over last-prompt regardless of position in the file.

Open Questions

  • Why was the v2.1.53 fix reverted in v2.1.55? Was it intentional or collateral?
  • Is #23061 (the oldest canonical) still being tracked internally at Anthropic?
  • Will the sessions-index.json schema be updated, or will the architecture shift to JSONL-only scanning?
  • Does Anthropic's internal tracker have a single issue for this, or is it as fragmented as the public repo?
interconnectedMe · 5 months ago

Update: additional failure modes discovered

Since filing this issue I've done a deeper investigation and found two more failure modes beyond the last-prompt overwrite described above. All three share the same root cause: the extension's title display relies on raw string scanning of the last 64KB of each JSONL file.

Failure Mode 2: 64KB tail window eviction

On long sessions (>64KB of content after the custom-title entry), the title entry gets pushed outside the scanner's window entirely. The extension falls back to lastPrompt or shows nothing. This is the same issue described in #33165.

Failure Mode 3: Cross-session content contamination

The scanner searches for "customTitle": " as a raw string — it does NOT verify it's reading a top-level JSONL entry. If a tool result or conversation message contains the literal string "customTitle": "some text" (e.g. when debugging session management), the extension picks up that text as the session's title. This causes one session's title to appear on a completely different session.

Updated workaround

The systemd timer fix described in the original report has been expanded:

  • Hook rewrite: The UserPromptSubmit hook now re-appends the correct custom-title on every prompt (not just the first), sourced from a persistent title-registry.json. This defeats both the 64KB eviction and the content contamination for active sessions.
  • CUSTITS signal: Manual titles are pinned by prefixing with CUSTITS in the sidebar rename — the timer detects this, strips the prefix, and records the title as permanent (never overwritten by automation).
  • Timer additions: --fix-task-titles mode backfills the registry from first user messages and re-asserts correct titles on inactive sessions every 2 minutes.

All three failure modes would be eliminated if titles were stored in a separate index file rather than scanned from conversation content.

Related issues

I've collected all the issues that stem from this same root cause — see the cross-post on #33165 for the full list.

bryan-9line · 5 months ago

Anthropic fix this please. work item to chat session correlation is impossible with this bug

interconnectedMe · 5 months ago

Update: Root cause traced to extension source — vj() two-pass scanner

I've traced this through the minified extension.js (confirmed against anthropic.claude-code-2.1.71-linux-x64). The display title isn't "last entry wins" as I initially described — it's more subtle.

The extension resolves titles via:

const title = vj(fileContent, "customTitle") || vj(fileContent, "lastPrompt") || fallback;

Where vj() scans with two sequential patterns — compact first, then spaced:

function vj(content, key) {
    const patterns = [`"${key}":"`, `"${key}": "`];  // compact, then spaced
    let result;
    for (const pat of patterns) {
        let idx = 0;
        while (true) {
            const pos = content.indexOf(pat, idx);
            if (pos < 0) break;
            result = extractValue(content, pos + pat.length);  // overwrites each time
            idx = ...;
        }
    }
    return result;  // last value found wins — spaced pass always runs second
}

The spaced pass runs after the compact pass and overwrites result on every match. So the last spaced-format occurrence always wins — even if it appears early in the file. The extension writes its own custom-title entry in spaced format at session start, so it permanently dominates any compact-format entries appended later. This explains why /rename appears to work initially but fails on reload [#25090] [#31394] — the appended entry uses compact format and loses to the earlier spaced entry on rescan.

Additionally, the extension only reads the last 64KB of each JSONL file (Z1 = 65536 constant). For long sessions, the original custom-title entry near byte 0 falls outside the tail window entirely, and vj() falls through to lastPrompt. This is the same mechanism reported in [#33165], which proposes storing titles in a separate index rather than relying on tail-scanning large JSONL files.

Three failure modes, one root cause:

| # | Failure | Mechanism | Also reported in |
|---|---------|-----------|-----------------|
| 1 | last-prompt overwrites title | Extension appends last-prompt after session close; no logic to prefer customTitle | [#25090], [#31394] |
| 2 | 64KB tail window eviction | Long sessions push custom-title outside the scanner's 64KB window | [#33165] |
| 3 | Cross-session content contamination | vj() does raw string search — a "customTitle": "..." string inside a tool result or conversation message gets picked up as the title | — |

Related: [#27202] found that /rename after /resume writes the custom-title entry into the wrong JSONL file entirely (the containing session, not the target sessionId), because the scanner ignores the sessionId field. [#31270] reports that sessions-index.json has no customTitle field, so custom names are lost on reload even when correctly persisted in JSONL.

Workaround (working on Linux): I have a Python script + systemd timer (every 2 min) that:

  • Simulates the extension's vj() scan including the 64KB tail window to detect mismatched titles
  • Appends corrected custom-title entries in spaced JSON format (so they win the two-pass scan)
  • Bumps file mtime by +1ms to trigger the extension's file-change detection, then a second pass restores the mtime to the session's last real content timestamp (to avoid corrupting sidebar ordering)
  • Skips sessions modified in the last 5 minutes (to avoid fighting VS Code's live writes)

Effectively within ~2 minutes of a session closing, the title is corrected — the timer fires every 2 min, and skips sessions modified in the last 5 min. So a closed session gets its title fixed on the next timer tick after the 5-minute cooldown. Worst case ~7 minutes, typical case ~2–5 minutes. Happy to share the full script if useful.

Suggested fix in the extension: Replace the vj() scan-order approach with semantic priority: if any customTitle entry exists in the tail window, use it regardless of format. lastPrompt should only be a fallback when no customTitle exists at all. Longer term, persist customTitle in sessions-index.json [as suggested in #31270 and #33165] so it survives both the 64KB window and reload cycles.

ojura · 4 months ago

Reinforcing this report with a mechanism explanation from reading the extension source (2.1.112):

The session-list metadata parser (lp() and the inline variant inside fetchSessions) only scans the last 64KB of the JSONL for customTitle / aiTitle / lastPrompt / summary. The resolver chain is:

title = customTitle || aiTitle || lastPrompt || summary || firstPrompt(head_64KB)

So customTitle does sit ahead of lastPrompt in the priority chain, but only if it's found. When a session grows past ~64KB after the custom-title entry, the entry falls out of the tail window. The parser then can't find it, falls through, and lastPrompt (which keeps getting re-written at the tail on every prompt) wins.

This explains both the position-dependent "last entry wins" behaviour observed here and why manual renames via the sidebar edit icon work (they write a fresh custom-title at the tail, back inside the window).

Observed across 19 sibling sessions in one project in #49996 (which I'm closing as a duplicate of this). Suggested fix: either index custom-title entries via a full-file scan (not tail-only), or move firstPrompt(head) ahead of lastPrompt in the priority chain so the default fallback is stable.

ojura · 4 months ago

Two follow-ups from applying the reorder fix locally on 2.1.120:

Empirical confirmation. Patched both resolver sites to put firstPrompt(head) ahead of lastPrompt (i.e. customTitle || aiTitle || firstPrompt(head) || summary || lastPrompt). After window reload, the sidebar immediately surfaced existing forks with their identity titles instead of all rendering whatever recent message happened to be in each one's tail-64KB. Forks I'd had open for weeks but couldn't visually distinguish became individually recognizable: same conversations, just stable titles. No other behavior changed; resume picker still has access to lastPrompt for "where did I leave off" via direct field access, and the chain is just no longer hijacking the title slot.

Two resolver sites, not one. In extension.js there are two near-identical title-resolver chains, both with the same wrong ordering:

  • Site 1: a standalone helper (the file-watcher / incremental list refresh path that recomputes a single session's title when its JSONL is updated). In 2.1.120 the chain reads let L = H || X5(G,"lastPrompt") || X5(G,"summary") || D; where H is customTitle/aiTitle and D is the precomputed firstPrompt result.
  • Site 2: directly inside async fetchSessions() (the sidebar-list rebuild). Chain reads let L = (...customTitle/aiTitle...) || x9(q,"lastPrompt") || x9(q,"summary") || Ca(N); where Ca(N) is the firstPrompt extractor on the head buffer.

Different extractor function names, otherwise the same shape. Whoever takes this upstream should patch both; fixing only one leaves the title flicker between sidebar refresh and file-watcher recompute. The duplication itself is suspicious; an extracted shared helper would prevent this from re-diverging later.

Variable names will change between releases; locate by structure (<...customTitle/aiTitle...> || <X>(...,"lastPrompt") || <X>(...,"summary") || <firstPromptFn>).

---

🤖 Reply written by Claude (claude-opus-4-7), posted by @ojura. The local fix lives in a personal patch-antigravity skill that re-applies a small set of surgical edits whenever the bundled extension updates. The reason this comment can name both resolver sites concretely instead of guessing: a public TS source dump matches this build symbol-for-symbol, which made it trivial to find the two near-identical chains and confirm the architecture before swapping the order. Working from readable source turns "I think this is the chain" into "here are the two specific sites that need to be patched together": the difference between a vague report and a one-PR fix.

showell-svg · 3 months ago

Same symptom on Claude Desktop / macOS, but possibly via a different code path than the VSCode last-prompt overwrite described above. Sharing as a second test case in case it helps localize the bug.

Repro on macOS Desktop

TEST_UUID=$(uuidgen)
claude --print --session-id "$TEST_UUID" --name "TEST TITLE" "Reply with exactly the word: ok"

# Verify the customTitle landed in the transcript:
find ~/.claude/projects -name "$TEST_UUID.jsonl" -exec head -1 {} \; | head -1 | python3 -m json.tool
# Expected: "type": "custom-title", "customTitle": "TEST TITLE"

# Open the session in Claude Desktop:
open "claude://resume?session=$TEST_UUID"

Expected: Desktop side panel shows TEST TITLE.

Actual:

  • Side panel: General coding session
  • Tab title (top of right pane): Untitled

What's different from the VSCode case

In my repro the JSONL has only the customTitle record as its first line plus the headless /review-mr exchange. There is no last-prompt record anywhere in the file, so Desktop isn't being misled by last-entry-wins; it's never reading the customTitle in the first place.

That suggests Desktop's session-import / title-resolution path doesn't consult type: "custom-title" records at all on macOS, separate from whatever's overriding them on VSCode after-the-fact.

Evidence Desktop knows about the record type

From strings /Applications/Claude.app/Contents/Resources/app.asar:

  • "custom-title" (record-type literal)
  • customTitle:A.trim() and customTitle:t.trim() (write-side handlers, presumably the right-click → rename path)

So Desktop has write-side handlers for customTitle but apparently no read-side resolver on import.

Why this matters

I run an automated review on every new GitLab MR for my team's repo via claude --print --session-id <uuid> --name "SPT MR REVIEW: !<iid>" "/review-mr <iid>". Every one of those sessions shows up in Desktop's side panel as "General coding session" instead of SPT MR REVIEW: !84. After a few days the side panel is unsearchable. Right-click rename works but is per-session manual toil.

Versions

  • Claude Desktop: 1.7196.0
  • Claude Code CLI: 2.1.141
  • macOS: 15.5 (24F74)

Happy to test patches or provide more context if useful.

littlemyx · 1 month ago

Still present on macOS CLI v2.1.220 (so it isn't only the VS Code extension), and there's a second-order effect that makes it considerably worse than "the name is ugly".

The prompt-derived name doesn't just get displayed — it gets persisted as a real custom-title record in the transcript. In my ~/.claude/projects/** I have several titles that are verbatim 75–80-character truncations of a first prompt, stored as ordinary title records, e.g.:

{"type":"custom-title","customTitle":"<first 79 chars of a user prompt, cut mid-word>","sessionId":"<uuid>"}

And since 2.1.204 a newly created session copies the previous session's title record into line 1 of its own file (see #78264, #80906), those truncated prompt strings then propagate to every child session. One such 79-character fragment ended up as the name of 5 unrelated sessions here; another spread to 2. Because a session that already has a title never gets an ai-title generated, the fragment is permanent in all of them.

So the two bugs compound: this one manufactures a garbage title, and the copy-on-new bug then replicates it across the picker.