[BUG] Remote Control (mobile): session silently hangs mid-execution; only local Esc recovers it — no remote unstick mechanism

Open 💬 15 comments Opened Apr 20, 2026 by GustavoVzla

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported as a bug yet (related: #47127 feature request for Cancel button, #28508 AskUserQuestion sync bug — both distinct from this)
  • [x] This is a single bug report
  • [x] I am using the latest version of Claude Code

What's Wrong?

When using Claude Code via Remote Control (claude.ai/code) from a mobile browser, the CLI session on my laptop silently enters a frozen state mid-execution and becomes permanently unresponsive to any input from the mobile client. The only recovery mechanism is pressing Esc (or any key) on the local terminal — no action from the mobile side can unstick it.

This is distinct from:

  • #47127 (feature request for a Cancel button to interrupt a visibly running tool)
  • #28508 (AskUserQuestion responses from mobile not propagating to CLI)

In my case there is no visible tool call in progress, no AskUserQuestion, no rate limit, no API error, no obvious trigger. The session just stops advancing after 2-4 normal message exchanges, and from the mobile client it looks like every new message is being sent but nothing ever comes back.

Reproduction pattern (consistent across multiple sessions/projects)

  1. Start Claude Code on laptop (VSCode integrated terminal, Windows 11)
  2. Leave the laptop at home, commute to a meeting / cafe / etc.
  3. Open claude.ai/code on mobile browser, connect Remote Control to the running session
  4. Send a message — works fine. Claude responds, runs tools (Bash, MCP, Write, Edit), completes
  5. Send another message — works fine
  6. After 2-4 successful exchanges, during what appears to be a routine turn, the session becomes unresponsive
  7. The mobile client continues to accept text input and send messages, but none of them advance the CLI. No new responses, no tool calls, no errors. Messages appear to queue silently.
  8. Attempting /clear, sending random text, reconnecting the Remote Control tab, refreshing the browser — none of these wake the session
  9. Hours later, upon returning home, I press Esc once in the local terminal → the session immediately resumes, processes the queued messages, and works normally again

Evidence the session is not truly hung

  • The CLI process stays alive and in a readable/writable state (proven by the fact that Esc locally revives it instantly)
  • No API errors are logged in the .jsonl session file during the frozen period
  • When I inspected the .jsonl after recovery, the last entry before the freeze was a successful tool_use completion — there was no sign of streaming stall or network failure
  • This is not #25979 (API streaming stall) because that requires kill -9 to recover, while my case is fixed by a single local keystroke

Hypothesis

The CLI input-handling loop appears to enter a state where it is waiting for a local TTY keystroke (possibly as a confirmation, prompt, or UI state flag) that Remote Control's input channel does not deliver. This would explain why local Esc instantly unsticks it while remote input does not.

A possible cause: whatever UI prompt/permission/spinner state the CLI enters after certain tool calls may require a synchronous keypress from the terminal's process.stdin raw mode, which the Remote Control bridge isn't wired to send.

What Should Happen?

  • Remote Control input should be fully equivalent to local TTY input — any state that local Esc/Enter can unstick should also be unstickable from mobile
  • At minimum, a visible "session frozen — local intervention required" indicator in the mobile UI so users aren't silently blocked
  • Ideally, automatic detection of "messages queued but not being processed" with a remote unstick mechanism

Business impact

I'm a solo founder (GT Dev Solutions, Madrid). Remote Control is a critical productivity feature for me — it's meant to let me keep working on client projects, reviewing deploys, and resolving urgent bugs while away from my laptop. In practice, with this bug, I can only rely on Remote Control for very short interactions (<5 messages) before needing to physically return home.

Yesterday (2026-04-19) I lost several hours of work while two separate sessions were frozen in this exact state. Both recovered instantly when I got home and pressed Esc. This has happened repeatedly over the past weeks — it's not an isolated incident.

Error Messages/Logs

No errors. The JSONL session file simply stops receiving new entries after the freeze point.

Steps to Reproduce

  1. Windows 11, Claude Code 2.1.112+ in VSCode integrated terminal
  2. Start a session with any project and MCP servers loaded (I have 18 MCPs configured globally, but the bug occurs even without any MCP calls)
  3. Leave the laptop running, connect from mobile browser via claude.ai/code Remote Control
  4. Interact normally — the freeze occurs non-deterministically after 2-4 exchanges, usually after (but not only after) a tool call completes
  5. Any further mobile input has no effect until local keypress

Claude Model

Opus

Is this a regression?

Possibly — Remote Control is relatively new. I don't have a known-working Claude Code version for this specific feature since adoption was gradual.

Last Working Version

_No response_

Claude Code Version

2.1.112 (also occurred in 2.1.108 and earlier 2.1.x)

Platform

Anthropic API (Max plan)

Operating System

Windows 11 (laptop/CLI side) + Android browser (mobile side)

Terminal/Shell

VSCode integrated terminal (bash/git-bash on Windows)

Additional Information

  • Happens consistently across projects (I have multiple parallel sessions for different clients)
  • Does NOT correlate with any specific tool — happens after Bash, Write, Edit, MCP calls, or even plain text responses
  • Does NOT correlate with rate limits (I'm on Max plan, and the freeze persists long after any rate window would reset)
  • Browser refresh on mobile does not re-sync — the issue is on the CLI side, not the web client
  • Related but distinct: #47127 (feature request for Cancel button), #28508 (AskUserQuestion mobile sync), #25979 (API streaming stall requiring kill -9)

Workaround: none remotely. Must physically access the laptop to press a key.

View original on GitHub ↗

15 Comments

InsaneTrilobyte · 2 months ago

Am also having this exact issue with the mobile app on android I've tried writing skills to prevent hangs but even with timeout and --kill-after=5 on every single command it still hangs and its only when remote control is active

alex-rosenberg35 · 2 months ago

I keep running into this or a similar issue. Whenever a tool or subprocess gets stuck for any reason, the entire session becomes blocked with no way to rescue it remotely. This is a massive blocker for effective use of remote-control.

ntabor82 · 2 months ago

I am also experiencing this issue, with increasing frequency. This morning, after it happened, I restarted the machine and restarted code with remote, and it froze again after only 15 min. I have noticed that the app has a bug, where on screen rotate or screen on/off, it will clear pop-ups on the android side (usually waiting for input), and I wonder if this is the cause.

davidwyly · 2 months ago

Experiencing this issue as well

GustavoVzla · 2 months ago

Workaround validated today: Telegram plugin as an alternate input channel that bypasses the freeze

Reporting back with a concrete workaround that confirms the underlying hypothesis (Remote Control's input bridge is not equivalent to local TTY) and gives users an interim recovery mechanism without physical access to the laptop. This does not fix the bug — the freeze still happens — but it provides a "remote Esc" via a different code path.

Setup

Add the official Telegram plugin to the same Claude Code session you use for Remote Control:

claude --channels plugin:telegram@claude-plugins-official

(Setup details in the plugin docs; I documented zombie-process gotchas in #36964.)

Why it bypasses the freeze

The Telegram plugin delivers user messages into the session via a different ingress than Remote Control. From what I observed today:

  • Remote Control's input goes through the cloud bridge → CLI's RC adapter → some internal queue. The freeze appears to be in this queue / RC adapter layer.
  • The Telegram plugin spawns a Bun MCP server that polls Telegram and delivers messages as user-prompt-submit-hook events directly into the session's event loop — same code path local TTY input ultimately reaches.
  • Sending any text from Telegram to the bot during a frozen state wakes the session in the same way that local Esc does: the queued Remote Control messages flush, the session resumes, and subsequent Remote Control input works again until the next freeze.

Test today (2026-04-29)

After 4 paired pings over a 36-min window (one of them after a 15-min idle gap), the Telegram channel stayed live and responsive while the same Claude process was also serving Remote Control. The freeze pattern I described in the issue did not manifest during the Telegram test — but more importantly, when freezes have happened in the past (without the plugin loaded), I had no way to recover remotely; with the plugin loaded, a single Telegram message from my phone would have given me the same recovery effect as local Esc.

Why this is a workaround, not a fix

The root cause described in this issue is unchanged:

  1. The freeze still happens with the plugin loaded — Telegram only gives you a way to unstick it remotely.
  2. Users without the plugin (or on plugin-incompatible setups) remain blocked.
  3. Adding a Telegram plugin to every Remote Control session adds setup complexity, requires a Telegram bot per session (single-consumer getUpdates limitation), and introduces a token to manage.
  4. The behavioral difference between RC input and TTY/plugin input is itself the bug — Remote Control should be input-equivalent to local TTY, full stop. Please don't close this issue as workaround-resolved.

What this tells us about the underlying bug

The fact that any alternate input channel (Telegram plugin → MCP user-prompt-hook) unsticks the session reinforces the hypothesis from the original report: Remote Control's input is being dropped or queued behind a state machine that local TTY input bypasses. The freeze is not a process hang — it's an input-routing bug specific to the RC adapter.

This narrows the fix target: the RC input path needs to be verified equivalent to process.stdin raw mode in all the states where the CLI's input handler may be waiting (permission prompts, post-tool spinners, AskUserQuestion, etc.). Whatever's blocking RC input but not TTY/plugin input is the bug.

Recommendation

Until a real fix lands, document this as an interim workaround in the Remote Control docs:

If your Remote Control session freezes and you cannot return to the laptop, having the Telegram plugin loaded with --channels provides an alternate input channel that can unstick frozen sessions. This is a workaround, not a fix — the underlying input-routing bug is tracked in #51267.

Happy to provide more reproduction data or test instrumented builds if useful.

koteitan · 1 month ago

Reproducible trigger: running /compact from the remote (mobile) client

I'm hitting what looks like the same bug, and I found a consistent trigger for it: sending /compact from the remote client.

Environment

  • Claude Code: 2.1.145
  • Local: Linux (WSL2), Claude Code CLI in a terminal
  • Remote: mobile browser via Remote Control

Reproduction

  1. Run /remote-control, then send /compact from the remote (mobile) client
  2. After a long wait, Compacted is displayed
  3. Send "test" from the remote → it shows as × test (looks like a send failure), no response from the agent
  4. Type "test2" locally → the agent responds to "test2"; the earlier "test" is invisible to the agent
  5. Without any further action, the earlier "test" then gets sent on its own (belatedly flushed)
  6. The agent responds to "test"
  7. From here, remote commands are accepted again

So the queued remote input is flushed the moment I do anything locally. Local input always works; only the remote path freezes. This matches the "blocking on a local TTY keystroke" hypothesis in the original report — the remote messages seem to be queued but not delivered until a local stdin event wakes the loop.

What I ruled out (A/B testing, restart + reproduce each time)

  • Disabling all hooks ("hooks": {}) → still freezes
  • Removing the entire user-level settings.json → still freezes

So it's independent of hooks, permissions, MCP servers, and the rest of settings.json. It really does look like the input-loop / TTY issue rather than anything in user config. Pressing Esc locally (as others reported) and any other local input both serve as the "unstick" event here.

Snailflyer · 1 month ago

The /compact repro is useful because it separates this from a generic “tool is slow” or API streaming stall. If local input immediately flushes queued remote input, then the live process is still healthy; the broken part is that the Remote Control input path is not waking the same TTY/input loop that local stdin wakes.

A good diagnostic split would be:

  1. Trigger the freeze from the mobile Remote Control path, e.g. after /compact.
  2. Do not type in the local terminal yet.
  3. From a separate control path that targets the same live terminal process, send a small input or interrupt into the same pane.
  4. Check whether the queued Remote Control messages flush.

If that wakes it, it strongly supports “Remote Control adapter/queue is not equivalent to local TTY input.” If it does not wake it, then the issue is deeper than just the RC input adapter.

This is also the class of problem I’ve been designing Faryo around: https://github.com/Snailflyer/faryo

Faryo is not a fix for this Claude Remote Control bug. It keeps Claude Code/Codex/shell inside the same tmux session and gives phone/desktop browsers a separate lightweight workbench for output review, short input, approve/interrupt, and handoff. So it may be useful as an A/B comparison or escape-hatch style control surface when the official Remote Control path queues input but the underlying tmux-backed process is still alive.

The important product requirement for the official fix is still exactly what this issue says: any state that local Esc, local text input, or another local TTY event can recover should have a remote-visible state and a remote recovery action. Silent queuing is the worst failure mode because the phone makes the user think the session is still reachable when the host-side input loop is actually waiting elsewhere.

honzastim · 1 month ago

Reporting a fresh occurrence + workaround stack — opened #61735 today which the duplicate-bot correctly flagged here. Adding 👍.

Variant: my case wasn't Remote Control specific — it was a pure ScheduleWakeup (dynamic /loop mode) silent death. Session terminated for unknown reason (cluster recycle / OOM / crash, no on-disk forensics). All pending wakeups died with the process because the queue is in-memory only.

Cost: Max plan subscription cancelled because of this single failure mode after a 3-day silent gap during a vacation (zdravim z plaze pings into the void).

Workaround stack shipped same-day, sharing in case it helps others:

  1. systemd watchdog (~80 LOC) — reads chronicle file + persistent-memory file mtime, sends Telegram alarm via existing bot if both > 4h stale during waking hours (CEST 08-22), with 2h cooldown to avoid spam.
  1. Telegram remote-interrupt (~150 LOC) — /claudes lists running claude sessions with PID + pts + age + CPU, /interrupt N sends os.kill(pid, SIGINT) to the N-th session. TIOCSTI is kernel-blocked on modern Linux (sysctl dev.tty.legacy_tiocsti=0) so we can't inject Esc keystroke directly, but SIGINT to PID is the equivalent of Ctrl+C in the TTY — Claude Code reads it and breaks out of current operation. Self-protection refuses to signal the bot's own PID.
  1. Crash-resume primitive that would fix this upstream: persist the ScheduleWakeup queue to ~/.claude/scheduled_tasks.json on each call, re-attach on session resume (similar to how CronCreate already persists).

@GustavoVzla validated a similar Telegram-as-alternate-input bridge above — same pattern. The two together (watchdog + remote-interrupt) get back ~95% of recovery capability without needing physical access.

— @honzastim

koteitan · 1 month ago

Quick follow-up: for me this turned out to be intermittent. Three phases since my report:

  • 🚨Initially (~May 20): froze as reported — remote input stuck, only local input flushed it.
  • ✅May 21: no freeze at all — remote input went through immediately, several times in a row.
  • 🚨May 22→23: froze again — a message I sent from remote stayed queued for almost a full day, and flushed only the instant I opened Claude locally.

So it's not 100% reproducible, but the bug is still present — and when it happens, only local input recovers it.

koteitan · 1 month ago

By the way, I made a workaround skill to avoid the issue with a kicking alerm by monitor+sleep before the compaction. https://github.com/koteitan/my-skills/tree/main/skills/compact-unfreeze

koxon · 1 month ago

+1 — Android (Claude Code app). Same input-hang pattern: after a few exchanges the session stops advancing remotely, every subsequent message from the phone just sits queued, and I have to physically return to the host machine and tap the terminal before it flushes and resumes. No visible tool in progress, no error in the mobile UI. The "Telegram-as-alternate-input bridge" workaround in the thread matches my observation that any local input wakes the session — RC input is the only path that doesn't.

homelabchaos · 1 month ago

Adding a matching data point from a different setup (also lines up with #47678 / #53328 / #54076):

Environment

  • Claude Code 2.1.157 (latest), Linux, launched with claude --remote-control inside a persistent tmux session.
  • Accessed via the Claude mobile (Android) app remote control, and via SSH/mosh → tmux.
  • Model: Claude Opus 4.8 (1M context).
  • MCP servers: several custom stdio servers + one local HTTP/SSE MCP server (localhost).

Repro

  1. Run claude --remote-control in tmux; connect from the mobile app.
  2. Trigger a tool call that takes ~10–60s — an MCP tool call (HTTP/SSE server) or a long-running tool.
  3. Mobile UI sits on the ⚒ Channeling (Nh Nm) spinner indefinitely (observed counter climbing into the hours).
  4. The call sometimes returns to the agent as [Tool result missing due to internal error].
  5. Press Escape on the local tmux TTY → session immediately resumes; retrying the call succeeds (the op had already completed server-side).

Notes

  • Most exposed on MCP / HTTP-SSE tool calls and long-running tools; short/light tool use over mobile is mostly fine.
  • Ruled out here: not permission prompts (allowlist confirmed — no prompt fires), not a stale render (Escape resumes interaction immediately), not network (HTTP connections healthy, no buffered data).
  • The [Tool result missing due to internal error] + "operation already succeeded, recovers only on local Esc" matches #53328's streaming-receive-race description specifically.

Happy to provide more detail or logs if useful.

andrisak-am · 1 month ago

Confirming this is still present in v2.1.158 (latest as of 2026-05-31), and adding a data point that extends the report.

Environment: Linux aarch64 (NVIDIA DGX Spark / GB10), Claude Code 2.1.158, driving a long-running agent session over Remote Control.

Symptom: mid-turn the session hangs ("combobulating" / no UI progress); input typed on the remote side is queued but not processed while the turn appears stuck. Happens most often during turns that run several back-to-back tool calls.

Recovery — the key addition: local Ctrl+C / Esc usually unsticks the queued input, but not always — in a meaningful fraction of cases the local interrupt does nothing and the only options are to wait it out or kill the terminal. So the current "only local Esc recovers it" assumption is optimistic: sometimes even local Esc does not recover.

Frequency: ~10 occurrences in a single multi-hour session — high enough to seriously disrupt remote-driven work.

Repro is just: drive a non-trivial agent task (multiple tool calls per turn) via Remote Control and watch for mid-turn stalls where remote input stops being consumed. A remote-side "unstick"/cancel signal (cf. #47127) would help, but the deeper bug is that the input queue stalls at all, and that local interrupt is not a reliable recovery.

santamaruka · 20 days ago

Adding a clean-environment reproduction with a clear upstream/downstream split, in case it helps narrow this down.

Environment: Windows 11, Claude Code 2.1.183 (autoUpdatesChannel: stable)

Downstream (display) fully alive, upstream (control) dead:

  • The session syncs perfectly to BOTH the mobile app and the claude.ai desktop app — latest messages/turns appear instantly (downstream is healthy).
  • Input sent from the app NEVER reaches the local session. Typed messages remain as unsent grey chips; tapping AskUserQuestion options in the app does not register on the host. So it's not a mid-execution hang per se — the control/upstream channel is simply not delivering.
  • This is a regression: Remote Control worked ~1 month ago.

The Esc workaround from this issue does NOT recover it in my case.

Everything else ruled out:

  • /login token refresh — no effect
  • /remote-control re-toggle + fully reopening the app — no effect
  • Rollback to 2.1.181 — no effect
  • Clean reset: a single claude.exe process, no residual/orphan sessions — still repros (so it's not stale-session contention)
  • claude doctor reports Remote Control ✔ (network/token are clean)
  • Session registration is correct: the URL shown by /remote-control (.../session_<id>) matches the running session ID, so it is not routing to a dead session.

Closed issues #37357 / #52084 / #52796 describe the same family of symptoms; reopening or linking may be warranted.

ajugland · 4 days ago

Adding corroborating data: 4 occurrences in one day across a multi-session tmux fleet (macOS, Claude Code 2.1.207, all sessions driven via tmux, /remote-control connected from mobile). Same signature as OP: process alive, transcript (<sid>.jsonl) goes completely silent, composer holds unsent text, all local keystrokes are no-ops (Enter/Ctrl-C/Ctrl-U/Esc/printable chars — note: unlike OP, Esc does not revive for us). tmux is exonerated: pane_in_mode=0, other panes fine, pasted text still renders in the composer.

Timing pattern (4/4, unusually consistent):

| # | last jsonl record (local time) | background subagent completes (SubagentStop) | stuck until |
|---|---|---|---|
| 1 | 08:42:48 | +5 s | manual restart |
| 2 | 13:33:35 | +7 s | restart at 13:44:54 |
| 3 | 11:14:02 | +5 s | restart ~2.5 h later |
| 4 | 14:03:26 | +7 s | restart 36 min later |

Three hard commonalities in all four:

  1. Last jsonl record is a normal, successful turn end (assistant text → stop_hook_summary → turn_duration). The wedge happens in the transition to idle, never mid-work.
  2. A background subagent finishes 5–7 seconds after that turn end (observed via SubagentStop hook). The window is strikingly similar in all four incidents.
  3. /remote-control was active in every case (footer /rc). Zero wedges ever observed in sessions without /rc; the wedge class appeared the same day /rc was first used in the fleet.

Additional observation (incident 3): while input was dead, new permission dialogs from still-running background agents kept rendering on screen (two different dialogs, ~11 min and ~2 h into the wedge) — rendering and the agent side stay alive; only local input handling is dead. The dialogs were unanswerable locally.

Recovery that works every time: kill -TERM <claude-pid> + claude --resume <session-id> — session comes back healthy with full context.

Repro attempts without a real remote client attached (headless /rc enabled, 6 dispatch cycles + 2 background-agent scenarios) did not trigger it, so a live remote-client/network event on the /rc channel seems to be a required ingredient.

Hypothesis based on the above: input-ownership handoff between the /rc bridge and the local TTY desyncs in a race window around turn-end when a SubagentStop lands 5–7 s later — local input loop loses ownership permanently while rendering continues.