Remote control session fails to resync after connection drop
Open 💬 17 comments Opened Feb 25, 2026 by dk0hn
Problem
When the remote control connection between the iOS app and a local Claude Code session drops (cause unclear — possibly backgrounding the app or network interruption):
- No indication the connection is lost — the session still appears "Interactive" on iOS
- Messages silently fail — user types and sends messages that appear to go through but are never received by the local session
- Spinner continues on iOS giving the impression the session is working
- Force quitting and reopening the iOS app shows the session as "Idle" but tapping into it loads the stale conversation state from before the disconnect (hours old in this case)
- Cannot resync — the only option is "New session" which loses all conversation context
Repro steps
- Start a Claude Code session on Mac with remote control enabled
- Connect from iOS Claude app — works fine initially
- Wait for connection to drop (backgrounding app, network change, or just time)
- Try sending a message from iOS — appears to send, no error shown
- Notice the local terminal never receives the message
- Force quit iOS app, reopen, tap into session
- Conversation shows stale state, not current
Expected behavior
- Detect the connection drop and show a clear "Disconnected" indicator
- Prevent message loss by showing an error when sending on a dead connection
- Resync the conversation state when reconnecting to an active session
- Fall back gracefully — if resync isn't possible, tell the user rather than showing stale state
Environment
- Claude Code 2.1.56
- iOS Claude app via remote control
- iPhone on WiFi, Mac on same network
17 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
same here
Same problem here. My setup:
What I observe:
Agreed - same issue here, although non-Mac based this time:
Only way to get it going again after it stops is to stop the session on the VM and restart it again. As soon as I do a new remote control, it works fine for a while before not responding again
+1, experiencing the same issue on the browser (claude.ai/code).
After leaving the session idle for ~10+ minutes:
Plan: Pro, macOS
Setup details:
Experiencing the same issue — remote control works fine initially but eventually drops and gets stuck on "remote control reconnecting" in the status bar. The session still appears as a live session in the Coding tab on claude.ai, but the link between that and the local Claude Code session drops for an unknown reason and doesn't reconnect.
this keeps happening to me too. no way to tell the connection dropped, messages just go nowhere. at least show a disconnect warning or something
in my case I do at least get told that the conversation disconnected -- in fact it seems to instantly get _archived_ when it disconnects. I would much rather it ... reconnect. The CLI side doesn't always seem to notice it's disconnected. And the behavior seems different between
/remote-controlandclaude remote-control. In the latter case, after it disconnected I pressed ctrl-C, and during shutdown it seemed to be trying to archive the session, and get confused that the session was already archived and the environment was gone.Root Cause Analysis from Debug Logs
I've traced the exact death sequence from local debug logs (
~/.claude/debug/*.txt) on Claude Code 2.1.71 (Linux, Ubuntu 24.04, desktop machine — no sleep/suspend involved).Timeline (March 9–10, 2026)
Root Cause
code=1002as permanent —"Transport permanently closed"— and stops attempting reconnection.POST .../stopwhich gets a 401 (token expired), refreshes the token, but never uses the fresh token to re-establish the transport."Transport not configured".The Bug
The
bridge:replmodule has no recovery path fromcode=1002. Once the transport is marked as permanently closed, it becomes a terminal state — the transport object is never re-created, even though:Environment
Linger=yes, no idle/kill policies, GNOME power set tosleep-inactive-ac-type=nothingExpected Fix
When the WebSocket closes with
code=1002and reconnection is exhausted, the bridge should re-register the environment and create a new HybridTransport instead of permanently giving up. The fresh token obtained after the 401 should be used to establish a new session ingress connection.Potentially relevant root cause evidence: the
session_ingressWebSocket gateway appears to enforce a ~25-minute max connection lifetime, causing server-initiated1006closures regardless of client activity.In a 6-hour session I observed 15 disconnects at consistent 25-minute intervals. The client reconnects successfully each time (attempt 1, ~1s), but work poll responses show
latest_heartbeat_at: nullafter reconnect — which likely explains why session state fails to resync.Full analysis with timestamps and log evidence in #34868.
I'm experiencing the same issue. Remote control sessions frequently fail to resync after connection drops, requiring a new session each time.
Highly unreliable. I have to go back to my desktop every 3 minutes. Useless as is.
If you need a workaround right now, this stale-session / silent-message-loss failure mode is exactly why I built pikiclaw. It runs Claude Code on your own machine but talks to it through Telegram, Feishu, or Weixin instead of depending on the built-in remote-control transport, with session switching/resume plus long-task sleep prevention, watchdog, and auto-restart for longer-running sessions. Quick try:
npx pikiclaw@latestGitHub: https://github.com/xiaotonng/pikiclaw
Title
Page reload during agentic coding orphans the session and freezes background subagents in the UI
Summary
When using claude.ai in the browser to drive a long-running agentic coding session (Claude Code with background subagents), refreshing the page — including a reflexive F5 on a localhost test app open in the same window — disconnects the UI from the in-flight session. The conversation stops updating, background subagent progress no longer surfaces, and there is no way to re-attach. The work itself appears to continue server-side, but the operator loses all visibility into it.
Reproduction steps
/running-the-loopor any prompt that dispatches a long-running subagent).python -m http.server 8000athttp://localhost:8000).Reproduces every time on the first refresh during an in-flight subagent.
Expected behavior
The page reload re-attaches to the existing session. Background subagents continue executing server-side, their token stream resumes in the UI, and any tool-call results that completed during the reload are surfaced in order. Conceptually the same model as a Jupyter notebook — the kernel keeps running, the page just re-binds to it.
Actual behavior
After the reload, the claude.ai tab shows the prior conversation as a static transcript, but:
The most painful part is that the subagent's work likely did complete server-side. The operator just has no way to see it, and no way to know which dispatched tasks are now orphaned.
Impact / why it matters
For agentic coding workflows where a single session dispatches subagents that run for minutes to hours, a lost UI binding is effectively lost work — not because the compute was wasted, but because the operator cannot recover the results without manually digging through logs or re-running the prompt. This is especially acute when claude.ai is functioning as the "remote control" for a Claude Code session that is also serving a test surface the operator needs to refresh repeatedly. The browser-window co-location makes F5 a near-guaranteed trigger.
The staleness is also hard to detect: nothing in the UI signals "you are now disconnected from your background tasks." The operator only notices when a subagent that should have responded 10 minutes ago is still silent.
Suggested solutions
Several options, ordered from most-comprehensive to least-invasive — let the team pick:
beforeunloadwarning when a subagent is in flight. Show the browser's "Leave site? Changes you made may not be saved" prompt whenever a background agent is active. Cheapest to implement; catches the reflexive F5 case directly.localhost:*or127.0.0.1:*URL via a tool call, offer a one-click "Open in new window" prompt. Doesn't fix the underlying disconnect, but removes the most common trigger.Options 1 + 2 together would be ideal; option 3 alone would already eliminate most accidental occurrences.
Environment
/running-the-loop, while a locally-served test surface (e.g.python -m http.server 8000) is open in the same browser for iterative validation.Frequency
Workflow-recurring, not intermittent. Reproduces on every page refresh that occurs while a background subagent is in flight. The browser-window co-location of the claude.ai tab and the test surface makes this a near-daily occurrence during autonomous-loop work.
@evanrwiley's page-reload variant is worth surfacing as the same failure class the OP describes — different transport, identical pathology:
| Trigger | Symptom | Recovery path |
|---|---|---|
| iOS remote-control connection drop (OP) | UI still shows "Interactive", messages silently dropped, force-reopen loads stale state, only "New session" works | None — context loss |
| Page reload during agentic coding (@evanrwiley) | Session orphaned, background subagents frozen in UI, no way to re-attach | None — context loss |
| Backgrounding the iOS app for >N seconds (@cdvv7788 "every 3 minutes") | Same as OP, just faster | None — context loss |
All three are downstream of the same architectural fact: session state is tightly coupled to the lifetime of the originating transport, not to a server-side authoritative record that any transport can re-attach to. The "stale state on reopen" detail in the OP is the giveaway — there is a persisted snapshot, it's just frozen at the moment the transport believed the session went idle, and the transport-side state is what's being shown rather than the live session-side state.
Two narrowing experiments worth running before this gets triaged as three separate bugs:
1. Does the local Claude Code session keep advancing while the iOS transport is dead? If yes, then the OP description ("messages silently fail") is wrong in one direction — the messages are being dropped by the transport, not by the session — and the session has work-product that the resync just isn't surfacing. That's a much smaller fix (re-attach + diff sync) than rebuilding session state.
2. If you have a long-running background subagent in flight when the page reloads (@evanrwiley's repro), is it still consuming tokens on the backend? If yes, you're paying for invisible work — and that's the symptom that will make this a priority because it's directly user-cost-visible, not just an ergonomic frustration.
The "Resync the conversation state when reconnecting to an active session" line in the OP's Expected Behavior is the load-bearing fix. xiaotonng's pikiclaw workaround (running CC locally and routing through Telegram/Feishu/Weixin instead of the built-in remote-control transport) works precisely because it sidesteps the transport-coupling-to-state assumption — the transport is dumb pipes; the session is the source of truth; reconnects are cheap. Whatever the eventual fix is, it should aim for that property, otherwise the next transport (web app, agent-view daemon, mobile CLI) will reproduce this from scratch.
One more — the OP says "the only option is 'New session' which loses all conversation context". The conversation
.jsonlfor that session almost certainly still exists in~/.claude/projects/**/*.jsonlon the host — losing context here is a UI affordance choice, not a data-availability one. Even before a real resync ships, a "Resume from local transcript" action would close the worst-case loss for users hit by this today.Same stale-render also happens on Desktop (macOS) after a reconnect — and I can confirm the underlying data is fully intact server-side, so this is purely a client render/cache problem, not data loss.
Scenario: session created on Desktop over SSH, driven from the iOS app with the laptop closed, then laptop reopened and Desktop reconnected.
Confirmed server-side: all messages — the mobile-sent ones, the assistant replies, and the later Desktop messages — are in a single transcript file (
<id>.jsonl), and the resumed process used--resume <id>without--fork-session. So it is one continuous session, no fork, nothing lost.Client symptom: after reconnecting, the Desktop chat view showed none of the mobile-originated exchange — leading me to wrongly conclude the chat had forked and my messages were gone. Closing and reopening the chat forces a re-read of the authoritative server-side transcript and everything reappears.
So on resume/reconnect the client should re-render from the server-side session rather than a stale local cache. Reproducible on Desktop, in addition to the iOS case described here.