[BUG] Desktop: Remote SSH session hangs after `Passing plugin(s) to SDK`, no server.spawn RPC ever issued

Status Fixed / completed
Reported on v2.1.111
Maintainer reply None cached
Activity 6 comments · opened Apr 24, 2026 · closed Apr 27, 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?

Claude Desktop on macOS 26.3.1 hangs indefinitely when starting a Remote (SSH) Claude Code session.

The hang occurs immediately after Desktop logs [CCD] Passing N plugin(s) to SDK. After that line the SDK never proceeds to spawn the remote CLI — no server.spawn RPC is ever sent to the remote server --bridge. All worker threads sit idle in mach_msg, so it is not a CPU hang but an async await that never resolves.

Reproducible every time, including with:

  • a completely fresh ~/Library/Application Support/Claude/ (full re-login),
  • zero remote MCP connectors (single plugin: skills),
  • empty session persistence.

A direct SSH + claude CLI workflow on the same remote host works normally, which isolates the bug to the Desktop-side SDK → CLI spawn path (not the remote host and not the network itself).

What Should Happen?

After [CCD] Passing N plugin(s) to SDK, the Desktop SDK should either:

  1. issue a server.spawn RPC over the bridge and start ccd-cli on the remote within a few seconds, OR
  2. fail with a clear timeout / error surfaced in the UI so the user can act on it.

An indefinite, silent hang with no error, no timeout, and no UI feedback is the worst failure mode — the user cannot tell whether work is progressing, queued, or dead.

Error Messages/Logs

# Mac side main.log — the hang point (reproduces every attempt)
[info] Starting local session local_XXXXXX in /home/tama/ai-agent-team
[info] [SSH] Using remote spawn function for session ... (cli: /home/tama/.claude/remote/ccd-cli/2.1.111)
[info] [CCD] Using skills plugin at: .../local-agent-mode-sessions/skills-plugin/...
[info] [CCD] Passing 1 plugin(s) to SDK (skills: 1, remote: 0, local: 0)
[info] [EventLogging] Flushing N events                ← periodic flush continues, but no more real progress
[info] LocalSessions.checkGhAvailable: cwd=...          ← periodic repeat only; no session ever starts

# Mac side ssh.log — network-interface flap correlates with hangs
[info] [remote] network-interface change detected; probing SSH controllers
[info] [remote] network-interface change detected; probing SSH controllers
...
# Earlier incident showed a 15-minute handshake timeout from the same cause:
[error] [SSH2Connection] Connection error: Timed out while waiting for handshake
[error] [RemoteServerController] Connection failed (899990ms, trigger: warm_up)

# Remote side bridge RPC log — shows Desktop connects fine, then goes silent:
[Server] RPC request: method=server.capabilities, id=1  → ok
[Server] RPC request: method=server.ping,          id=2  → ok
[Server] RPC request: method=files.list,           id=3-5 → ok
[Server] RPC request: method=files.read,           id=6-7 → ok
(silence — no server.spawn RPC ever arrives)

Steps to Reproduce

  1. macOS 26.3.1 (arm64 Apple Silicon) with Claude Desktop 1.3883.0.
  2. Configure a Remote SSH host for Claude Code (Hetzner VPS, Linux amd64, ~300ms RTT).
  3. Launch Desktop. SSH handshake + bridge channel open in ~15s (logs [BinaryDeployment] Bridge channel opened, Connected to remote server). So far, so good.
  4. Send any prompt (even an empty ping) to kick off a session. Desktop logs:

Starting local session local_XXXXXX in <cwd>
[SSH] Using remote spawn function for session ... (cli: ~/.claude/remote/ccd-cli/2.1.111)
[CCD] Using skills plugin at: ...
[CCD] Passing 1 plugin(s) to SDK (skills: 1, remote: 0, local: 0)

  1. UI spins forever. No error. No further real log entries — only periodic EventLogging Flushing and checkGhAvailable repeats. On the remote, remote-server.log confirms no server.spawn RPC ever arrives.

Persists after each of the following, tried in isolation:

  • clearing ~/Library/Application Support/Claude/local-agent-mode-sessions/
  • clearing claude-code-sessions/
  • clearing claude-code/ and claude-code-vm/ CLI caches
  • clearing local-agent-mode-sessions/skills-plugin/
  • moving the entire ~/Library/Application Support/Claude/ aside and re-logging in
  • disabling every Connector on claude.ai/settings/connectors (plugin load = skills only, remote: 0)
  • swapping the Desktop-deployed binary at ~/.claude/remote/ccd-cli/2.1.111 with the content of the user's native ~/.local/share/claude/versions/2.1.119 (still hangs — rules out "version-specific to 2.1.111")

Claude Model

Not sure / Multiple models

Is this a regression?

Yes, this worked in a previous version

Last Working Version

Unknown — Desktop 1.3883.0 initially worked for Remote SSH sessions to the same host earlier today (and in previous days/weeks); the hang started appearing without an obvious trigger, possibly correlated with a macOS network-interface flap event.

Claude Code Version

2.1.119 (Claude Code) — user's native install used by direct SSH, which works fine 2.1.111 — Desktop-deployed CLI at ~/.claude/remote/ccd-cli/2.1.111, which hangs

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Other

Additional Information

sample(1) of hung Desktop process

All threads idle in mach_msg — no stuck CPU loop, consistent with an unresolved async await:

  • Main thread[NSApplication run] → _nextEventMatchingEventMask → _CFRunLoopRun → mach_msg2_trap (100% idle).
  • libuv-worker × 4 — all in mach_msg.
  • tokio-runtime-worker × 8 — all in mach_msg.
  • V8Worker × 3 — all in mach_msg.

Experiment: CLI version swap rules out version specificity

Replaced the Desktop-deployed ccd-cli/2.1.111 binary with the content of the user's working versions/2.1.119 (filename kept as 2.1.111 so Desktop's [BinaryDeployment] CLI v2.1.111 cached path accepts it). Still hangs identically. This rules out "older CLI version" as the root cause and points at the Desktop → CLI spawn bridge, or at state/handshake happening before the child process is actually invoked — consistent with the fact that no server.spawn RPC is ever sent.

Correlated signal: network-interface flapping

Mac ssh.log shows [remote] network-interface change detected; probing SSH controllers events in the same window as hangs, including during an earlier separate 15-minute SSH handshake timeout (Connection failed (899990ms, trigger: warm_up)). Hypothesis: in-flight SSH/HTTP calls spawned by the SDK are orphaned on interface flap and lack a watchdog timeout, so the SDK's internal await never settles.

Related (but distinct) issues

  • #52734 — Desktop SSH session hangs 3–10 min on "Starting session…" after a utun network-interface flap. Same Desktop 1.3883 and same deployed ccd-cli/2.1.111, but the hang is before Passing plugin(s) to SDK (in [remote] probing SSH controllers) and recovers on its own after a few minutes. This report is different: hang is after Passing plugin(s) to SDK, no server.spawn RPC is ever issued, and it never recovers.
  • #52122 — flagged by auto-triage as a possible duplicate of #52734.

Common thread across all three: Desktop 1.3883 + remote-spawn pipeline on macOS, different phases of the pipeline stuck.

Full logs and diagnostics

Complete main.log, ssh.log, sample output, and remote remote-server.log are available in a private GitHub repo. Happy to share directly with Anthropic engineers — please comment or DM for access.

The logs contain per-account identifiers so are not included inline here.

Environment (extended)

| Component | Version |
|---|---|
| Claude Desktop | 1.3883.0 |
| CCD / Claude Code CLI (Desktop-deployed) | 2.1.111 |
| Claude Code CLI (native, working via direct SSH) | 2.1.119 |
| macOS | 26.3.1 (25D2128), arm64 Apple Silicon (MacBook Air) |
| Node (bundled in Electron) | 24.14.0 |
| Remote host | Linux amd64, ~300ms RTT from client |
| Remote claude-ssh binary | 9a871e263f5ad97516c97c8c8129f94990635ae7 (built 2026-04-21) |

View original on GitHub ↗

6 Comments

github-actions[bot] · 4 months ago

Found 2 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/52122
  2. https://github.com/anthropics/claude-code/issues/25664

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

0xbrainkid · 4 months ago

This is a strong remote-startup bug because it isolates the failure to a very specific bridge boundary: Desktop reaches the point of handing plugins to the SDK, but never crosses into the actual remote server.spawn step. That means the problem is not generic SSH reachability or remote CLI viability; those have already been partially ruled out by the direct SSH + claude success path.

The absence of timeout or surfaced error makes it especially costly. Users are left in a state where nothing progresses and nothing explains itself, which is one of the hardest classes of remote orchestration failure to reason about.

From the operator perspective, this is not just a hang. It is a dead start path on the Desktop-side remote-session control plane.

Tama831 · 4 months ago

Thanks @0xbrainkid — your framing captures it exactly. "Dead start path on the Desktop-side remote-session control plane" is a much better description than my original wording. Happy to update the issue text if helpful.

On the auto-triage bot: neither #52122 nor #25664 is a duplicate of this one, though #25664 is genuinely adjacent and worth reading alongside.

  • #25664 (open, assigned to @amorriscode, CC 2.1.42): ccd-cli is spawned on the remote and then hangs trying to load local macOS plugin paths (/Users/<user>/.claude/plugins/cache/...) that don't exist on the remote filesystem. The hang is downstream of server.spawn, inside the child CLI process, and at least one plugin path is present in the command line.
  • This issue (#52790) (Desktop 1.3883 + ccd-cli/2.1.111): ccd-cli is never spawned. No server.spawn RPC ever reaches the remote server --bridge (confirmed via the remote-side RPC log — only capabilities, ping, files.list, files.read arrive, then silence). The hang is before any child process exists. The plugin count at the hang point is skills: 1, remote: 0, local: 0 — i.e. no local or remote MCP plugins in play, just the built-in skills plugin.

So #25664 and #52790 are at different phases of the same Desktop → remote-spawn pipeline, with different states at the moment of hang. They may share a root-cause area (plugin/handshake handling around the Desktop→SDK→ccd-cli boundary), but the user-visible failure is different and the fixes are likely independent.

Please don't auto-close as a duplicate — treating these as separate will surface both phases of the pipeline to the fix owner.
Private logs + sample + RPC trace already prepared in a private repo (mentioned in the body). Happy to add Anthropic engineers as collaborators.

cyb213 · 4 months ago

Reproducing this on Windows 11 (MSIX), same Passing plugin(s) to SDK hang with the same claude-ssh 9a871e263... build on the remote. Cross-platform, not macOS-specific.

Regression pinpointed to MSIX auto-update at 2026-04-24 14:48:23 UTC

Client main.log:

2026-04-24 14:48:23 [info] beforeQuitForUpdate handler fired, going down for update
2026-04-24 14:48:54 [info] Starting app { appVersion: '1.3883.0', ... }
2026-04-24 14:48:55 [info] [CCD] Initialized with version 2.1.111

Client ssh.logClaudeSSHManager commit flipped across this restart:

BEFORE: cee98937dc257e6af64e712d71dbe4497eb82452 (built 2026-04-14T16:53:35Z)
AFTER:  9a871e263f5ad97516c97c8c8129f94990635ae7 (built 2026-04-21T17:07:27Z)

Every SSH session before 14:48 on the same Windows machine, same VPS, same user, worked and logged the full [RemoteProcess:…] Creating process / Spawning / Spawn confirmed sequence. Every session after the update stops at [RemoteServerController] Connected to remote server with no [RemoteProcess] line ever emitted — exactly @Tama831's "no server.spawn RPC ever issued" on macOS. MSIX update is the only change; no VPS-side changes, no config changes.

Windows-side hang log, matching the macOS sequence

[SSH] Using remote spawn function for session ... (cli: /home/cyb/.claude/remote/ccd-cli/2.1.111)
[CCD] Using skills plugin at: C:\Users\...\Roaming\Claude\local-agent-mode-sessions\skills-plugin\...
[CCD] Passing 1 plugin(s) to SDK (skills: 1, remote: 0, local: 0)
... (periodic LocalSessions.checkGhAvailable / EventLogging only, no spawn) ...
[warn] [CCD] SSH plugin sync failed; continuing without synced plugins: No response from server
    at O (...\app.asar\.vite\build\index.js:1343:38110)
    at r.cleanup (...\app.asar\.vite\build\index.js:1343:61245)
    at Socket.<anonymous> (...\app.asar\.vite\build\index.js:1343:74978)

The SSH plugin sync failed warning fires only from the socket-cleanup handler, i.e. after the user kills the app. The fallback path continuing without synced plugins is aspirational in the log text but never actually exercised during normal runtime — the awaited Promise never resolves, so spawn is blocked indefinitely. Adding a real timeout with UI feedback (per @Tama831's ask #2) is urgently needed.

VPS-side RPC trace — identical to @Tama831's

/home/cyb/.claude/remote/remote-server.log:

Claude remote server listening on /home/cyb/.claude/remote/rpc.sock
2026/04/24 12:12:07 [Server] New connection from: @
2026/04/24 12:12:08 RPC request: method=server.capabilities, id=1
2026/04/24 12:12:08 RPC request: method=server.ping,          id=2
2026/04/24 12:12:10 RPC request: method=files.read,           id=3
2026/04/24 12:12:10 RPC request: method=files.read,           id=4
2026/04/24 12:13:00 [Server] Connection closed: @

Capabilities, ping, two files.reads (folder browser) — then 50s of silence — then client-side close. No server.spawn, no plugin-sync method ever reaches the server. Same pattern as the macOS report.

Environment

| Component | Value |
|---|---|
| Claude Desktop | 1.3883.0.0 (MSIX, Microsoft Store / Claude_pzs8sxrjxfjjc) |
| Desktop CCD | 2.1.111 |
| OS | Windows 11 Home 10.0.26200 |
| Remote | Ubuntu VPS, amd64, non-root user (cyb@…) over Tailscale |
| Remote claude-ssh | 9a871e263f5ad97516c97c8c8129f94990635ae7 (uploaded by the updated desktop on 2026-04-24 14:49:06) |
| Remote ccd-cli | 2.1.111 (unchanged from before the regression) |

Additional cross-checks

  • Claude Code via the VSCode extension on the same VPS, same user, same network works end-to-end in parallel with the hang. Isolates the defect to the desktop app's remote-spawn control plane (as @Tama831 also noted with direct ssh + claude).
  • Non-root, so not the --allow-dangerously-skip-permissions root issue (#36739).
  • Renaming %LOCALAPPDATA%\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming\Claude\local-agent-mode-sessions\skills-plugin is useless — on next launch the app re-downloads 5 skills from the cloud profile and logs [SkillsPlugin] Delta: 5 to download, 0 to remove, then hangs with Passing 1 plugin(s) to SDK again. Skills can't be fully disabled from the user UI (visible UI lists only 1 of the 5 counted by the client). Only fix available to users right now is the VSCode remote path or downgrading the desktop app.

Confirms this is a cross-platform Desktop regression in the 2026-04-21 claude-ssh build (9a871e263...), triggered for Windows users by the MSIX auto-update pushed on 2026-04-24. Please don't auto-close as duplicate.

Tama831 · 4 months ago

Thanks @cyb213 — this is a major corroboration. To help maintainers avoid triaging this as OS-specific or user-config noise, summarizing the combined evidence from both reports, plus related same-window issues.

Established facts from the combined reports

  1. Cross-platform regression. Identical symptom reproduces on macOS (Apple Silicon, my original report) and Windows 11 (MSIX, x64). Not OS-specific — the defect is in the Desktop app's JS/TS control plane, not in any platform-native layer.
  1. Remote configuration also matches. Both reports: Tailscale → Ubuntu VPS over SSH, non-root user, ccd-cli/2.1.111. Desktop side is macOS 26.3.1 and Windows 11 Home 10.0.26200 respectively. The only shared variables are Tailscale + Ubuntu VPS + Desktop remote SSH; everything else is independent (Tailscale itself is addressed in "Cross-reference with same-window issues" below).
  1. Regression window narrowed to a 1-week band in claude-ssh.
  • Last-known-good: cee98937dc257e6af64e712d71dbe4497eb82452 (built 2026-04-14T16:53:35Z)
  • First-bad: 9a871e263f5ad97516c97c8c8129f94990635ae7 (built 2026-04-21T17:07:27Z)
  • On the Windows side, the MSIX auto-update at 2026-04-24 14:48:23 UTC flipped the bundled claude-ssh from good to bad, and the hang started on the very next session — same VPS, same user, nothing else changed. A bisect across the diff between these two builds should isolate the change.
  1. The defect sits at least upstream of server.spawn on the Desktop-app side. On the same VPS, same user, at the same time, Claude Code invoked via the VSCode remote extension works end-to-end. VPS-side remote-server logs on both reports show server.capabilities + server.ping + a couple of files.read RPCs, then silence — the client never issues server.spawn at all. The remote server stays healthy and idle; the client never reaches the point of sending that RPC (so the health of the server-side server.spawn handler itself can't be judged from this observation, since no call arrives).
  1. The fallback branch isn't running at runtime, judging by the logs. The SSH plugin sync failed; continuing without synced plugins: No response from server warning is observed only from the socket-cleanup handler (i.e., when the user force-quits the app). During normal execution, neither that warning nor the continuing without synced plugins behavior that should follow it appears; the plugin-sync awaited Promise neither resolves nor rejects, spawn is blocked indefinitely, and no UI feedback is shown. Without having read the source I won't claim this definitively, but the log behavior suggests the happy-path fallback branch is unreachable at runtime.
  1. A gap exists between the Settings UI and the actual skill count. The Settings screen lists only 1 skill, but [SkillsPlugin] Delta: 5 to download, 0 to remove in the logs shows 5 skills are synced from the cloud profile. Because of this gap, users have no in-app way to disable the skill that's triggering the hang.

Cross-reference with same-window issues

To avoid this being treated as an isolated case, listing related recent reports in order of relevance.

  • #51021 — After the 2.1.113+ native-binary spawn rewrite (changelog: "Changed the CLI to spawn a native Claude Code binary (via a per-platform optional dependency) instead of bundled JavaScript"), Playwright MCP (stdio) hangs / times out on Linux over Tailscale. A path that worked in 2.1.85 broke in 2.1.114, with the same shape of failure: "the client's subprocess-startup lifecycle has an await that never resolves." Related siblings from the same rewrite: HTTP MCP timeout (#50289) and Windows subprocess-init regression (#50559).
  • #48565 — A separate regression where claude-ssh moving from 9e5781dbcee98937 removed the password-auth fallback. Combined with the present report, this means claude-ssh has shipped at least two behavior-breaking changes in roughly two weeks (9e5781db → cee98937 → 9a871e263).
  • #43852 — macOS → Windows/WSL2 SSH remote where SSH auth succeeds but the server binary is never exec'd. Same "server.spawn is never issued" shape as this issue; the trigger differs (#43852 involves a username containing a space, over a ZeroTier overlay), but the lineage — "the client-side exec/spawn path silently fails" — lines up.
  • #52734 — A different pattern where Starting session… hangs for 3–10 minutes after a utun flap. This issue is a permanent hang, so the symptom is different, but as a nearby-in-time bug in the same log phase the two may be related.

Across this issue (Tailscale), #51021 (Tailscale), and #43852 (ZeroTier), the common thread is VPN/overlay transport + silent failure on the subprocess/spawn path — not a Tailscale fault per se, but a situation where the latency/packet pacing of a VPN overlay appears to expose a race in the post-rewrite spawn logic. Taken together, the circumstantial evidence suggests this isn't an isolated bug but one sitting in the blast radius of the 2.1.113+ native-binary spawn rewrite and the recent claude-ssh changes (causation pending a proper bisect).

Reproduction, mitigations, and resolution criteria

Minimum repro config (for internal reproduction):

  • Ubuntu VPS / EC2 etc. (non-root user) + Tailscale overlay
  • Desktop app 1.3883.0 (macOS or MSIX) + CCD 2.1.111 + claude-ssh 9a871e263...
  • At least one skill attached to the cloud profile

With that, the hang should reproduce after Passing 1 plugin(s) to SDK at session start, with no spawn ever following. If it doesn't reproduce, variables worth varying: skill count, substituting a direct SSH path for Tailscale, and the cloud profile's plugin configuration.

Current mitigations. Realistic options are (a) switching to VSCode Remote, or (b) downgrading the Desktop app to a build older than the 2026-04-21 claude-ssh. (a) is functionally degraded — Desktop's integrated features (skills / plugin UI / cowork / file pane) are unavailable. (b) is possible on macOS if an older DMG is on hand, but on Windows MSIX, pinning to an older version is effectively impossible for end users given how the Store distributes it, which leaves Windows users with only VSCode Remote. Deleting the local skills-plugin directory has no effect — the app re-downloads from the cloud profile at next launch.

Resolution criteria (what we'd consider sufficient to close this issue):

  1. The next claude-ssh build introduces an explicit timeout on the plugin-sync await.
  2. On timeout, either UI feedback is shown, or the continuing without synced plugins fallback branch is reachable at runtime.
  3. With that build, the minimum repro config above no longer hangs.

Preventive suggestions (optional). claude-ssh has shipped at least two behavior-breaking regressions in ~2 weeks (#48565 + this one), and end users currently have no way to defer the auto-update. Would be grateful if the team could look at (i) a kill switch / rollback path when a breaking regression is detected, (ii) splitting the Desktop claude-ssh build channel into stable / beta, and (iii) adding client-side telemetry on critical-path awaits like plugin-sync (timeout counts, fallback-branch hit rate) — so the next silent hang of this shape surfaces quickly with a measurable blast radius.

Evidence bundle

  • My Mac-side logs, sample dumps of the hung process, and VPS-side remote-server.log RPC trace live in the private repo linked from the original post. Happy to grant access or mirror the relevant excerpts into this issue if it helps a maintainer — just say the word.
  • @cyb213, if useful I can merge your Windows main.log / ssh.log excerpts into the same repo alongside the macOS artifacts so there's a single place for anyone debugging this to pull both platforms' traces. Happy to set up a folder for you or accept a PR — whichever you prefer.

---

Please don't auto-close as duplicate of the older SSH-flakiness issues (#52734, #52122). Could a maintainer share where triage stands against the 9a871e263 ↔ cee98937 claude-ssh diff and the broader 2.1.113 native-binary spawn rewrite? This currently blocks the Desktop remote Claude Code workflow for affected users with no in-app mitigation, and we'd appreciate triage priority reflecting that.

github-actions[bot] · 3 months ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.