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):

  1. No indication the connection is lost — the session still appears "Interactive" on iOS
  2. Messages silently fail — user types and sends messages that appear to go through but are never received by the local session
  3. Spinner continues on iOS giving the impression the session is working
  4. 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)
  5. Cannot resync — the only option is "New session" which loses all conversation context

Repro steps

  1. Start a Claude Code session on Mac with remote control enabled
  2. Connect from iOS Claude app — works fine initially
  3. Wait for connection to drop (backgrounding app, network change, or just time)
  4. Try sending a message from iOS — appears to send, no error shown
  5. Notice the local terminal never receives the message
  6. Force quit iOS app, reopen, tap into session
  7. 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

View original on GitHub ↗

17 Comments

github-actions[bot] · 4 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/28532
  2. https://github.com/anthropics/claude-code/issues/28402
  3. https://github.com/anthropics/claude-code/issues/28508

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

kishisan · 4 months ago

same here

inner-exception · 4 months ago

Same problem here. My setup:

  • Claude Code running 24/7 in a tmux session on a Mac Mini
  • Remote control enabled with /remote-control
  • Claude Android app connected via the QR code
  • Claude Code web app connected via the URL (or by just navigating to https://claude.ai/code)

What I observe:

  • Talking to Claude Code via the Android app or the web app works fine for a few hours
  • Then, after sending a prompt, Claude is shown as "thinking" (e.g. "Pondering...") forever on both clients, with no possibility to stop or send new messages
  • When I check Claude Code on the Mac Mini, it already finished thinking a while ago
  • The only way to get out of this is to reset the remote control on the Mac Mini by using /remote-control, disconnect, then establish a new session
dcgsteve · 4 months ago

Agreed - same issue here, although non-Mac based this time:

  • Claude Code running 24/7 in a tmux session on a Ubuntu 24.04 VM (on Proxmox)
  • Remote control enabled with /remote-control
  • Claude Code web app connected via the URL (or by just navigating to https://claude.ai/code)

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

kingsleywang1984 · 4 months ago

+1, experiencing the same issue on the browser (claude.ai/code).

After leaving the session idle for ~10+ minutes:

  • Remote → Local still works (local terminal receives messages)
  • Local → Remote is broken (agent output / permission prompts not visible on browser)
  • Browser shows "Reconnect" but clicking it does not restore the output stream

Plan: Pro, macOS

theglove44 · 4 months ago

Setup details:

  • Claude Code: 2.1.70
  • macOS 26.3 (arm64 / Apple Silicon)
  • Node.js v20.19.5

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.

wowitsjack · 4 months ago

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

gwillen · 4 months ago

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-control and claude 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.

Henderson11 · 4 months ago

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)

11:54:53 UTC  Last successful "Sending 1 message(s) via transport"
12:01:55      Ingress transport connected (normal reconnect)
12:27:03      Ingress transport connected (~25min reconnect cycle, normal)
...           (reconnects every ~25 minutes throughout the afternoon)
17:43:15      Last successful "Ingress transport connected"

18:08:23  ★ Transport permanently closed: code=1002
18:08:23    Reconnect exhausted (code=1002), falling back to poll loop
18:08:23    POST .../work/{sessionId}/stop force=false
18:08:24    StopWork: 401 received, attempting token refresh
18:08:26    StopWork: Token refreshed, retrying request

... bridge enters poll-only mode, transport is NEVER rebuilt ...

(next day)
01:37:42  Transport not configured, dropping 1 message(s) [user]
          (poll shows 4600+ consecutive empty polls, all returning "no work")
07:38:00  Still dropping all messages — transport permanently dead

Root Cause

  1. WebSocket close code 1002 (protocol error) is received at 18:08:23 — approximately 25 minutes after the last successful reconnect, suggesting the server-side closes the connection.
  2. The client treats code=1002 as permanent"Transport permanently closed" — and stops attempting reconnection.
  3. It then calls POST .../stop which gets a 401 (token expired), refreshes the token, but never uses the fresh token to re-establish the transport.
  4. The bridge falls back to poll-only mode, but since the session was stopped, polls return "no work" indefinitely.
  5. All subsequent messages (both user and assistant) are silently dropped with "Transport not configured".

The Bug

The bridge:repl module has no recovery path from code=1002. Once the transport is marked as permanently closed, it becomes a terminal state — the transport object is never re-created, even though:

  • The local process is still alive and running
  • The poll loop is still active (reaching 4600+ empty polls)
  • Token refresh succeeds

Environment

  • Claude Code: 2.1.71
  • OS: Ubuntu (Linux 6.17.0-1011-oem), desktop — not a laptop, no sleep/wake involved
  • The machine has Linger=yes, no idle/kill policies, GNOME power set to sleep-inactive-ac-type=nothing
  • Network is stable; the issue is not a local network dropout

Expected Fix

When the WebSocket closes with code=1002 and 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.

theglove44 · 4 months ago

Potentially relevant root cause evidence: the session_ingress WebSocket gateway appears to enforce a ~25-minute max connection lifetime, causing server-initiated 1006 closures 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: null after reconnect — which likely explains why session state fails to resync.

Full analysis with timestamps and log evidence in #34868.

darkneo29 · 3 months ago

I'm experiencing the same issue. Remote control sessions frequently fail to resync after connection drops, requiring a new session each time.

cdvv7788 · 3 months ago

Highly unreliable. I have to go back to my desktop every 3 minutes. Useless as is.

xiaotonng · 3 months ago

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@latest

GitHub: https://github.com/xiaotonng/pikiclaw

evanrwiley · 2 months ago

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

  1. Open claude.ai in a browser tab and start a Claude Code agentic session (e.g. via /running-the-loop or any prompt that dispatches a long-running subagent).
  2. While a subagent is in flight, open a locally-served test surface in the same browser window (e.g. python -m http.server 8000 at http://localhost:8000).
  3. Make a change to the test app and reflexively hit F5 to refresh the localhost page — or hit F5 while focused on the claude.ai tab.
  4. Return to the claude.ai tab.

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 token stream from the in-flight assistant turn never resumes.
  • Background subagent progress events stop appearing — no tool calls, no status updates, no completion notification.
  • The composer accepts input, but sending a message either starts a brand-new turn (losing context of the in-flight work) or appears to hang.
  • There is no visible affordance saying "this session is disconnected, click to resume" — the UI looks normal but is silently stale.

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:

  1. Server-side session persistence with auto-reattach. Background agents and tool calls run server-side independent of the browser tab. On page load, the client checks for an active session ID (cookie / local storage) and re-binds the websocket/SSE stream automatically. Closest analogue: Jupyter kernels surviving page reloads. Highest engineering cost, cleanest user experience.
  1. "Resume session" UX on reload. On page load, detect an in-flight session and offer a "Resume session" button or banner instead of silently showing a stale transcript. Even if full auto-reattach is hard, an explicit affordance ("3 background tasks still running — reconnect?") would let the operator recover deliberately.
  1. beforeunload warning 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.
  1. Suggest separating the test surface. When claude.ai detects the agent has opened a localhost:* or 127.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

  • Surface: claude.ai web app (not Claude Code CLI or IDE extensions).
  • Browser / OS: Web browser on Windows 11.
  • Use case: Agentic coding workflow — claude.ai drives a Claude Code session that dispatches long-running subagents via /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.

kcarriedo · 2 months ago

@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 .jsonl for that session almost certainly still exists in ~/.claude/projects/**/*.jsonl on 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.

evanrwiley · 1 month ago
  1. Does the local Claude Code session keep advancing - Yes but can't tell till you ask: Are you Hung or still working then you might get a response. but you never know if claude is done. you see the * L helps spinning
  1. If you have a long-running background subagent: no way to tell except if you get concerned I ask claude whats in process. I could be wrong but I think it might be the sub agents that are causing the problem. Perception make me think it all works till a subagent call is made or being used or the subagent is not giving control back, or not updating the remote session
joshua-dantonio · 1 month ago

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.