/desktop reports success but session never appears in Claude Desktop (sessionId=null + LSHandler misregistration on macOS)
Summary
/desktop reports Session transferred to Claude Desktop to the user, but the session never appears in Claude Desktop. Two distinct bugs are stacked:
- URL scheme misregistered against the wrong bundle ID (intermittent, environment-dependent on macOS).
- Session ID always resolves to
nullon the Desktop side, regardless of whether the URL was delivered correctly (architectural — appears to affect all CLI-to-Desktop handoffs).
Result: even when (1) doesn't apply, (2) silently breaks the handoff, and the CLI's success message is misleading.
Environment
- macOS (Apple Silicon)
- Claude Code CLI: 2.1.126
- Claude Desktop: 1.5354.0
- Claude Desktop's embedded Claude Code SDK (in VM): 2.1.121
Bug 1 — \claude://\ URL scheme bound to helper subprocess
\~/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist\ had:
\\\\
LSHandlerRoleAll = "com.anthropic.claudefordesktop.helper"
LSHandlerURLScheme = "claude"
\\
The \.helper\ bundle ID belongs to the sandboxed Electron helper subprocesses (\Claude Helper.app\, \Claude Helper (Renderer).app\, etc.) that don't handle URL events. The main app's bundle ID is \com.anthropic.claudefordesktop\ (no \.helper\).
Result: \open "claude://anything"\ returns \_LSOpenURLsWithCompletionHandler() failed with error -600 for the URL claude://\ (procNotFound). The CLI nevertheless prints \Session transferred to Claude Desktop\.
\lsregister -dump\ confirms only the main bundle claims the scheme:
\\\\
path: /Applications/Claude.app
identifier: com.anthropic.claudefordesktop
claimed schemes: claude:
\\
So nothing in the static registration explains why the user pref points at the helper. Suspected cause: somewhere during install or runtime, Claude.app calls a setDefaultProtocolClient-equivalent and passes the helper's bundle ID. Could not confirm from outside the app.
Workaround: patch the plist to point at the main bundle:
\\\bash\
python3 - <<'PYEOF'
import plistlib, pathlib, os
p = pathlib.Path(os.path.expanduser("~/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist"))
d = plistlib.loads(p.read_bytes())
for h in d.get("LSHandlers", []):
if h.get("LSHandlerURLScheme") == "claude":
h["LSHandlerRoleAll"] = "com.anthropic.claudefordesktop"
p.write_bytes(plistlib.dumps(d))
PYEOF
killall cfprefsd
\\
After that, \open "claude://test"\ no longer returns -600 and Claude.app launches correctly.
Bug 2 — \sessionId\ always resolves to \null\ on the Desktop side
Even when (1) is resolved and the URL is delivered, Claude Desktop's main log shows:
\\\\
[CCD] LocalSessions.setFocusedSession: sessionId=null
\\
…on every single \/desktop\ invocation. Across multiple \/desktop\ calls in different sessions, only one \setFocusedSession\ event in the entire log resolved to a real session ID; every other entry (290+ events) is \null\.
Suspected cause: CLI sessions are stored at \~/.claude/projects/<encoded-cwd>/<uuid>/\, but Claude Desktop's Claude Code runs in an Apple Virtualization Framework VM with its own SDK install at \~/Library/Application Support/Claude/claude-code-vm/<version>/\. The VM-side Claude Code does not appear to mount or proxy the host's \~/.claude/projects/\ directory, so when it tries to load the session ID emitted by the host CLI it can't find the session file → \null\.
This is the more important bug — even if Bug 1 is fixed, Bug 2 silently breaks the handoff.
Reproduce
- Start an interactive Claude Code session in terminal.
- Run \
/desktop\. - Observe CLI prints \
Session transferred to Claude Desktop\. - \
tail -f ~/Library/Logs/Claude/main.log | grep setFocusedSession\while running step 2. - Observe \
sessionId=null\. - Claude Desktop's window does not surface the transferred session.
Suggested fixes
- Don't print \
Session transferred to Claude Desktop\until Desktop has acknowledged a non-null session ID. Alternatively: \Failed to transfer session — see ~/Library/Logs/Claude/main.log\when \setFocusedSession\returns null. - Verify Desktop's URL handler registers under the main bundle ID, not \
.helper\. - Either share the host's \
~/.claude/projects/\into the VM, or have \/desktop\upload/import the session into the VM's storage before firing the deeplink.
Additional logs
\detectedProjects\ errors (\sqlite3 via disclaimer exited with code 14\) are common in main.log but appear unrelated — they're from Desktop's project-discovery scanning of VS Code/Cursor/Zed databases, not from \/desktop\ itself.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗