[BUG] Remote Control multi-session restore (activeSessionIds) is never written on an ungraceful shutdown — the exact case it exists for

Status Open
Reported on v2.1.231
Maintainer reply None cached
Activity 1 comment · opened Aug 30, 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?

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)

  1. Start a Remote Control server (--spawn mode) and attach two sessions to it.
  2. Confirm the server log shows both sessions active.
  3. kill -9 the server process (not a graceful quit/reload).
  4. Restart it.
  5. Only the session named in bridge-pointer.json's sessionId reconnects. The second session's client shows computer_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 --help describes --continue as resuming "the session last recorded for this directory" and --session-id as 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.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗