remote-control server spawns sessions via its resolved install path — every auto-update breaks new-session spawns with ENOENT until the server is restarted
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 worktreeservers (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
- The server does not exit or surface any problem at update time — it stays
Readyand 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. - Failed spawns leave orphaned
bridge-cse_*worktrees behind. - 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-controlserver 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.