[BUG] Remote Control multi-session restore (activeSessionIds) is never written on an ungraceful shutdown — the exact case it exists for
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?
Expected
A Remote Control server with multiple sessions attached restores all of them after the host process dies unexpectedly (crash, kill, container rebuild, VM destroy, host reboot) — or at minimum, the restore list is kept continuously up to date rather than only written at a clean exit.
Actual
Only the single session recorded as the pointer's primary sessionId reconnects. Every other session that was live at the time of death answers Remote Control host unreachable (computer_unreachable) on the mobile/web side, even though its transcript and Redis-side binding are both fully intact.
Root cause (traced in the shipped bundle, versions 2.1.231 / 2.1.236 / 2.1.251)
bridge-pointer.json supports a multi-session restore list, activeSessionIds. The consumer of that list is unconditional — it's installed whenever the field is non-empty. The producer, however, only writes it when the shutdown cause is one of upgrade, reload, or yield:
let p = e.persistActiveSessionsOnShutdown && e.preserveOnShutdown && !et && e.ownsPointer
? await e.awaitShutdownCause?.() : null;
if (p !== "upgrade" && p !== "reload" && p !== "yield") return;
A process that is killed rather than shut down gracefully never reaches this write. That is precisely the situation a multi-session restore list is for — an in-app upgrade or reload already has an orderly teardown path; a kill does not.
Repro (minimal)
- Start a Remote Control server (
--spawnmode) and attach two sessions to it. - Confirm the server log shows both sessions active.
kill -9the server process (not a graceful quit/reload).- Restart it.
- Only the session named in
bridge-pointer.json'ssessionIdreconnects. The second session's client showscomputer_unreachable, even though its session data is untouched on disk and its lock/binding are still valid.
Why this looks like a small, well-scoped fix
- The data is never actually lost — only the re-binding is missing. Manually reattaching the orphaned session with
claude remote-control --session-id session_<id>from its own
working directory works immediately, proving the session and its backend binding are both still good; the only thing missing is that a killed server never tells the reconnect logic to look for it. (This can't be combined with --spawn, so a spawn-mode server has no path to recover it on its own.)
- The consumer-side code to reattach a list of sessions already exists and already works — it's exercised on every graceful
upgrade/reload/yield. The gap is purely that the producer never runs for the ungraceful case. - No documented alternative exists:
claude remote-control --helpdescribes--continueas resuming "the session last recorded for this directory" and--session-idas taking exactly one id — both singular, neither covers restoring a set.
What Should Happen?
Either (a) persist activeSessionIds continuously rather than only at a graceful-shutdown checkpoint, or (b) additionally treat process-kill / next-startup as a trigger to reconcile the pointer against whatever sessions the backend still shows bound, rather than gating entirely on a captured shutdown cause.
Error Messages/Logs
Steps to Reproduce
setup claude remote control inside your dev container; start chat sessions remotely; rebuild your devcontainer
Claude Model
Opus
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.251
Platform
Anthropic API
Operating System
Ubuntu/Debian Linux
Terminal/Shell
Windows Terminal
Additional Information
Related
#68606 (closed, not planned) reported the single-session version of this same gap — independent analysis of that report converged on the same root cause (pointer only updated on graceful paths). This issue adds the multi-session angle, the exact gating logic, and a live before/after repro, in case that's useful context for reconsidering the general fix.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗