remote-control server spawns sessions via its resolved install path — every auto-update breaks new-session spawns with ENOENT until the server is restarted

Status Open
Reported on v2.1.224
Maintainer reply None cached
Activity 0 comments · opened Aug 7, 2026

Environment

  • macOS 15.x (Darwin 25.6.0), Apple Silicon
  • Claude Code 2.1.224 (native installer); servers were started from the npm-global install a few days earlier
  • Two long-running claude remote-control --spawn worktree servers (one per repo), driven from the iOS app

What happened

Two remote-control servers had been running since Aug 4, launched from the npm-global install. On Aug 7 at 09:16 the CLI migrated to the native installer (~/.local/bin/claude -> ~/.local/share/claude/versions/2.1.224), which removed the npm package directory.

The running servers survived (their binary stays mapped after unlink) and continued to report Ready. But hours later, every new-session prompt sent from the iOS app failed instantly:

[14:38:31] Session failed: spawn error: ENOENT: no such file or directory, posix_spawn '/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/bin/claude.exe' cse_01FyrTc2u6YVAJ6jhz9RuAnR
[14:38:31] kept worktree .../.claude/worktrees/bridge-cse_01FyrTc2u6YVAJ6jhz9RuAnR · session crashed
[14:38:31] Error: Failed to stop work cse_01FyrTc2u6YVAJ6jhz9RuAnR: StopWork: Authentication failed (401): Invalid authentication credentials. Remote Control is only available with claude.ai subscriptions. Please use /login to sign in with your claude.ai account.
·✔︎· Ready · <repo>

The server spawns each new session as a child CLI at the path resolved at server startup — a path that no longer existed. The prompts sent from the phone were lost. Note the misleading follow-on 401 ("Remote Control is only available with claude.ai subscriptions") from the stale server's in-memory auth state — the account's auth was actually fine.

Why this recurs on every update (not just the npm->native migration)

Under the native installer, each version is a separate version-numbered binary (~/.local/share/claude/versions/<v>), and the old version is pruned when a new one lands (verified: only one version present in versions/; the desktop app's claude-code/<v> channel likewise pruned a version a still-running session was using). Since the server spawns children at its resolved own path, any auto-update that occurs while a remote-control server is running strands it: the next new-session spawn hits the same ENOENT.

This is worse than the npm era, where updates rewrote the package in place at a stable path.

Why it's sneaky

  1. The server does not exit or surface any problem at update time — it stays Ready and only fails when the next spawn request arrives, possibly days later, from a phone where the user can't see the server log. A restart-wrapper loop doesn't help because the process never exits.
  2. Failed spawns leave orphaned bridge-cse_* worktrees behind.
  3. The 401 secondary error misdirects the user toward a subscription/login problem.

Suggested fixes

  • Spawn child sessions via the stable launcher path (~/.local/bin/claude) or re-resolve the current install at spawn time, rather than the version-pinned path captured at server startup.
  • Alternatively (or additionally): have the remote-control server detect that its install version/path has changed out from under it and either self-restart via the new binary or at least log a prominent warning and report a clear error to the mobile client instead of a crashed session.
  • Consider not pruning a version that a live process is still running from, until it exits.

Workaround

Restart the remote-control servers after every CLI update (and after the npm->native migration). They then spawn sessions normally.

View original on GitHub ↗