[BUG] claude://resume duplicates a session already open as a native Desktop tab; no deep link to focus an existing native session

Status Open
Maintainer reply None cached
Activity 6 comments · opened Jul 24, 2026

Summary

claude://resume?session=<cli-session-id> imports a CLI session by minting a new Desktop session whose id is hardcoded to local_<cliSessionId>, and de-duplicates purely on that id key. As a consequence:

  1. If the same CLI session is already open as a native Desktop Code tab (created inside Desktop, not via an earlier resume import), the deep link cannot find it and creates a duplicate tab — a snapshot fork of identical content that no longer stays in sync with the live native session.
  2. There is no deep link (or any documented URL) to focus an existing native Desktop session by id, so external integrations can only ever activate the app, never navigate to a specific native tab.

This is adjacent to #79309 (Desktop "Open in terminal" also spawns a new session) but on the inbound external→Desktop path.

Environment

  • Claude Desktop 1.24012.1 (macOS)
  • claude:// URL scheme, resume route

Steps to reproduce

  1. In Claude Desktop, open a Code session natively (New → Code). Note its content.
  2. Find that session's CLI session id (the UUID in the transcript filename under ~/.claude/projects/<project>/<uuid>.jsonl, which Desktop stores as cliSessionId in ~/Library/Application Support/Claude/claude-code-sessions/<account>/<org>/local_*.json).
  3. Trigger open "claude://resume?session=<uuid>".

Expected: the existing native tab is focused.
Actual: a second "General coding session" tab appears with duplicated content; the original native tab is untouched.

Root cause (from the shipped bundle)

importCliSession de-dupes on a key convention, not on the cliSessionId property:

const r = `${LOCAL_SESSION_PREFIX}${e}`;      // "local_" + cliSessionId
if (this.sessions.get(r)) {                    // key lookup only
    this.unarchiveSession(r);
    return r;                                   // → focuses the tab
}
// else: read transcript, create a new session whose id is set to r
  • A session created by resume gets id = local_<cliSessionId>, so a second resume with the same id hits the key and focuses — this is why re-importing works.
  • A native Desktop session gets id = local_<random-uuid>; its cliSessionId lives only as a stored property. The key local_<cliSessionId> is never present, so the lookup always misses and duplicates.

The de-dup data (cliSessionId) is already persisted on every session record — the lookup simply doesn't consult it.

Suggested fix (either one)

  1. In importCliSession, before creating a new session, also look up any existing session (native included) whose stored cliSessionId === e; if found, focus it instead of importing.
  2. Or expose a dedicated focus route, e.g. claude://claude-code-desktop/focus?cliSessionId=<uuid>, that navigates to an existing native session without importing.

Either would let external tools (companion apps, launchers, hardware remotes) deep-link users to the actual running session rather than a stale snapshot copy.

Context

Building a companion app that surfaces active Claude Code sessions and offers "open this session in Desktop". resume works great for headless / CLI-only sessions (gives them a UI), and re-focusing an already-imported session works — but native Desktop sessions can only be app-activated, and blindly calling resume on them silently duplicates. A focus-by-cliSessionId path would close the gap.

View original on GitHub ↗

3 Comments

jwbth · 28 days ago

This renders claude://resume useless for many use cases, or at least undermines the convenience it was created to bring. Anthropic did add a deep-linking capability but only left it at a skeleton stage. Even if you import, what use is the "General coding session" title (#83051)? And I don't think a separate "focus" route is required. Focusing should work out of the box.

BasedGPT · 27 days ago

This is a duplicate session-identity case rather than missing transcript data. The native Desktop tab keeps cliSessionId as a stored property under a different local session key, while claude://resume looks for local_<cliSessionId>, so the lookup misses and imports a second snapshot.

I built BasedGPT/claude-code-session-recovery for this duplicate-metadata family. From the affected macOS machine, run python tools/diagnose.py first and keep the output. It should identify whether two session records share the same cliSessionId and print the exact safe repair command. Do not apply a write-bearing command blindly.

Preserve the native session record and do not delete either tab or its metadata before the inventory is complete. The cleanup_synth_duplicates.py route is the relevant boundary only when the diagnosis confirms duplicate metadata sharing a cliSessionId; it is dry-run by default and should be applied only after Desktop is fully closed.

Hope this helps, if my tools are able to help you, would appreciate a ⭐ :)

jwbth · 27 days ago

Note that this duplication also kills the session's worktree if it was in one: once you open a worktree-attached session, it gets moved from the worktree folder under ~/.claude/projects to the main one. The real session then continues to leave traces of its prior existence in the original file. I just found one of my sessions duplicated across folders, and the original one had only this in its .jsonl:

{"type":"last-prompt","lastPrompt":"No need to have any button groups","leafUuid":"122a22b0-dea2-4d4c-8be2-59048823dc17","sessionId":"4adbe4d1-233d-4a1b-bb39-cadecce54f5a"}
{"type":"custom-title","customTitle":"Session title icon buttons","sessionId":"4adbe4d1-233d-4a1b-bb39-cadecce54f5a"}
{"type":"mode","mode":"normal","sessionId":"4adbe4d1-233d-4a1b-bb39-cadecce54f5a"}

Showing cached comments. Read the full discussion on GitHub ↗