[FEATURE] Un-gate claude://code/continue?session=<local_id> — validation and lookup are already implemented
Summary
claude://code/continue?session=<local_session_id> already contains a complete implementation for focusing a specific desktop session — parameter validation, exact-ID lookup, and route resolution are all present in the shipped app — but the handler returns early behind a feature gate, so the link only brings the app to the foreground and stays on whatever session was last focused.
Please consider enabling this path (or exposing an equivalent documented one).
Use case
A session finishes work (or a scheduled reminder fires) and pushes a message to my phone. I have a dozen sessions open. The notification tells me something happened, but not which session said it — so I have to hunt through the sidebar.
The fix is for the notification to carry a "return to the sender session" link. That works end to end today except for the final hop.
What I found
Environment: Claude Desktop 1.37937.3, Claude Code 2.1.150, macOS 26.5.2.
1. claude://code/continue?session=<id> — validation and lookup are implemented, but gated
The URL handler validates the parameter:
function bBt(e){ return e === `last` || (e !== null && yBt.test(e)) }
// yBt = /^local_[A-Za-z0-9-]{1,64}$/
A local_… session ID passes this check. The resolver does an exact match and produces a route:
async function SBt(e){
const t = await $j();
await t.waitForSessionsLoaded();
const n = (await t.getAllSessions()).filter(s => !s.isArchived);
const r = e === `last`
? n.sort((a,b) => (b.lastActivityAt ?? 0) - (a.lastActivityAt ?? 0))[0]
: n.find(s => s.sessionId === e); // <- exact ID match already supported
return r ? t.getSessionRoute(r.sessionId) : undefined;
}
But the entry point returns before any of that runs:
async function xBt(e,t,n,r,i){
const a = wM();
if (!await Mzt()) { P.info(`claudeURLHandler: code entry deep link gated off`); return }
...
}
Observed in ~/Library/Logs/Claude/main.log on every click:
claudeURLHandler: code entry deep link gated off
Result: the app comes to the foreground but does not switch sessions. From a user's point of view the link looks broken, with no error shown.
2. claude://resume?session=<cli_session_uuid> works, but duplicates instead of focusing
This path is not gated and does fire:
Resume deep link: importing CLI session 6df6752e-…
It calls importCliSession(), which creates a new session entry from the CLI transcript. The original desktop session stays where it is, and the sidebar now has two entries with the same conversation. Usable as a workaround, but it pollutes the session list every time.
3. The documented deep link cannot do this
Launch sessions from links covers claude-cli://open, registered by the CLI, which accepts only q, cwd, and repo. It starts a new terminal session; there is no parameter for resuming an existing one.
Why this issue rather than a +1 on the existing ones
#50345 (closed) asked for exactly this and reported that --resume <uuid> and various schemes were ignored. #81202 asks for the Cowork equivalent.
The new information here is that this is not a missing feature — it is a disabled one. The ID format is accepted, the lookup is exact, and the route is computed. Only Mzt() stands between the current behaviour and the desired one.
Request
Any of these would solve it:
- Enable the gate for
claude://code/continue?session=<local_session_id>. - Make
claude://resumefocus an existing session when the transcript is already imported, instead of importing a duplicate. - Document a supported equivalent, so integrations do not have to rely on internals.
Workaround in the meantime
A local HTTP redirect service maps a short code to the session ID and 302s to the claude:// URL, and the notification includes the session's title so the right sidebar entry can at least be identified by name. Everything works up to the final hop.
4 Comments
Follow-up after digging further. Two additions that narrow this down.
claude://resumecannot be used as a workaround for app-created sessionsThe dedup guard in
adoptCliSessiononly recognises an existing session when its ID is literallylocal_<cli_session_id>:That holds for sessions the CLI created, but not for sessions created inside the desktop app, which get an independent
sessionIdand carry the CLI transcript ID separately ascliSessionId.On my machine, out of 142 sessions in
~/Library/Application Support/Claude/claude-code-sessions/, exactly 1 satisfiedsessionId === "local_" + cliSessionId— and that one was a fork. For the other 141,claude://resume?session=<cliSessionId>imports a duplicate rather than focusing the existing session.So the practical picture is:
resumereliably duplicates, andcode/continueis gated. Neither is usable for "return to the session that sent this notification".&cwd=does not helpSome write-ups mention
claude://resume?session=<uuid>&cwd=<path>. In this build the Resume branch reads onlysession;cwdis never accessed, so adding it changes nothing.The gate looks account-scoped rather than local
Mzt()resolves throughjzt(), which is keyed onaccountUuidand resolves a numeric gate ID. If that is right, users cannot enable this locally even for testing — which is worth stating explicitly in the docs, since the URL is registered and silently does nothing when the gate is off. A visible message ("this deep link is not enabled for your account") would have saved a fair amount of debugging: the current behaviour is that the app comes to the foreground on the previously focused session, which is indistinguishable from a broken link.Adjacent prior art
claude-control solves the equivalent problem for terminal-hosted sessions by focusing the terminal tab (AppleScript TTY matching for Terminal.app/iTerm2, remote control for kitty,
wezterm clifor WezTerm,tmux send-keys). There is no equivalent for sessions living inside the desktop app, which is what this request is about.Independent repro on a newer build — same gate.
Confirming this on Claude Desktop
1.44121.2/ Claude Code2.1.255/ macOS 26 (Darwin 25.6). Same behavior exactly:claude://code/continue?session=<local_…>logsclaudeURLHandler: code entry deep link gated offand only foregrounds the app.Worth adding that the dev override is a dead end for normal users —
Qgt(the force-on gate set) is built fromCLAUDE_DEV_FORCE_GATES, but it's short-circuited to an empty set whenapp.isPackaged, so there's no way to self-enable this on a production install.Concrete use case: I built a local dashboard over
~/.claude/sessions+ the CCD store (claude-code-sessions/) that shows every live/open session and which ones are waiting on you. ResolvingcliSessionId → local_ sessionIdfor the deep link works perfectly — the only missing hop is this gate. Un-gating it (or exposing a documented "focus existing session" link that doesn't duplicate the wayclaude://resumedoes) would let notifications and any multi-session tool jump straight to the right chat. +1.Update: works on Claude Desktop
2.37337.1(macOS 26 / Darwin 25.5).claude://code/continue?session=<local_id>now switches to the target session. I tested it by switching to another session, confirming it was the focused pane, and then switching back.main.logno longer showscode entry deep link gated off.In this build the check is no longer a hard-off gate. It passes when the user is logged in and deep links haven't been disabled by policy.
The app also exposes its own per-session link now,
claude://claude.ai/epitaxy/<local_id>, which also lands on the right session.I'll leave closing to the maintainers, since the earlier report on
1.44121.2may still apply to 1.x builds.Follow-up to my earlier report: resolved for me too.
Retested on Claude Desktop
2.37337.1/ Claude Code2.1.300/ macOS 26 (Darwin 25.6). This is the same machine that reproduced the gate on1.44121.2.claude://code/continue?session=<local_id>now switches to the target session. I opened a link to a second session, then a link back to the first. ThelastFocusedAtof each session record changed 3 s apart, which matches the two clicks.main.logshows nocode entry deep link gated offline. The last one in my rotated logs is from 2026-09-04.The dashboard use case I described above now works end to end, and I have removed the gate caveat from it. I agree with @jacobchang-Nineder: maintainers can close this if 1.x builds are no longer a concern.