Remote SSH: desktop app parks on "Reconnecting" for hours after a successful reconnect, and never retries

Status Open
Reported on v2.1.222
Maintainer reply None cached
Activity 0 comments · opened Aug 10, 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?

In remote SSH mode, the desktop app can sit on Reconnecting to user@host… for hours after the reconnect has already succeeded at every layer, and it never retries.

I instrumented the remote at 1 Hz and caught a full occurrence. The session was wedged for 5 hours 8 minutes. During that entire window:

  • the SSH transport was up and carrying data — 74 KB moved across it
  • longest interval without a received byte: 14 seconds
  • NIC carrier up at every single sample, zero link flaps
  • zero Timeout, client not responding from sshd
  • a server --bridge process was alive and attached to the persistent --serve daemon
  • the daemon received zero requests for the whole 5 hours
  • not one new connection attempt — the app made exactly one reconnect and then parked

So this is not a transport failure and not a retry-budget exhaustion. The transport recovered in 10 seconds and the UI never noticed.

The trigger in my case was systemctl reload ssh on the remote (SIGHUP to sshd, which does not kill existing child sessions). 79 seconds later the app closed its connection on its own initiative (SSH2_DISCONNECT_BY_APPLICATION) and wedged on the way back. Temporal correlation is strong but I cannot prove causation — the app also initiates its own disconnects at irregular intervals, which I have logged ranging from 3 minutes to 32 hours apart.

Related: #74793 — same symptom string, same platform, same area, and its author reaches the same hypothesis (client-side liveness check misjudging a healthy-but-quiet session). Filing separately because the retry behaviour is the opposite: that report shows a reconnect storm (6 connections in 33 seconds), mine shows a single attempt followed by total silence. My data may be the "parked" variant of the same underlying state machine, or a distinct bug. Happy for maintainers to merge these if they turn out to be one thing.

What Should Happen?

Once the bridge attaches to the daemon, the UI should leave the "Reconnecting" state. Failing that, the app should keep retrying and recover on its own rather than parking indefinitely over a working connection.

Error Messages/Logs

Server-side sshd, around the reconnect (UTC):

06:23:49  sshd: Received SIGHUP; restarting.            <- systemctl reload ssh on the remote
06:23:49  sshd: Server listening on 0.0.0.0 port 22.
06:25:08  sshd: Received disconnect from <client>:11:   <- app closed it (BY_APPLICATION)
06:25:08  sshd: Accepted publickey ...
06:25:08  sshd: Accepted publickey ...
06:25:08  sshd: fatal: ssh_packet_send_debug: send DEBUG: Connection reset by peer
06:25:18  sshd: Accepted publickey ...                  <- the bridge connection

~/.claude/remote/run/<id>/remote-server.log — the bridge attaches, then the daemon hears nothing for five hours:

2026/08/10 06:25:08 [Server] Connection closed: @
2026/08/10 06:25:19 [Server] New connection from: @     <- bridge ATTACHED
                    ... nothing at all, 5h08m ...
2026/08/10 11:33:16 [Server] Connection closed: @       <- I restarted the app
2026/08/10 11:33:29 [Server] New connection from: @

<details>
<summary>Watcher log, 1 Hz sampling on the remote, full wedge window</summary>

Labels are in Romanian (my own tooling): puls = heartbeat every 15 min, carrier = NIC link state, conns = established SSH connections from the client, tăcere_max = longest interval without a received byte in seconds, bridge/cli = live server --bridge and ccd-cli process counts.

=== the reconnect at 06:25 — bridge back in 10 seconds, then nothing ===
2026-08-10T06:25:12+00:00 CONN         SSH from client: 2 -> 1
2026-08-10T06:25:12+00:00 BRIDGE       --bridge: 1 -> 0
2026-08-10T06:25:22+00:00 CONN         SSH from client: 1 -> 2
2026-08-10T06:25:22+00:00 BRIDGE       --bridge: 0 -> 1

=== 5 hours of heartbeats: link up, connections alive, bridge attached ===
=== NOT ONE new connection attempt in the whole window ===
2026-08-10T06:36:48+00:00 puls  carrier=1 conns=2 tăcere_max=3s  bridge=1 cli=0
2026-08-10T06:51:56+00:00 puls  carrier=1 conns=2 tăcere_max=10s bridge=1 cli=0
2026-08-10T07:07:04+00:00 puls  carrier=1 conns=2 tăcere_max=2s  bridge=1 cli=0
2026-08-10T07:22:12+00:00 puls  carrier=1 conns=2 tăcere_max=9s  bridge=1 cli=0
2026-08-10T07:37:20+00:00 puls  carrier=1 conns=2 tăcere_max=1s  bridge=1 cli=0
2026-08-10T07:52:27+00:00 puls  carrier=1 conns=2 tăcere_max=8s  bridge=1 cli=0
2026-08-10T08:07:35+00:00 puls  carrier=1 conns=2 tăcere_max=4s  bridge=1 cli=0
2026-08-10T08:22:43+00:00 puls  carrier=1 conns=2 tăcere_max=7s  bridge=1 cli=0
2026-08-10T08:37:51+00:00 puls  carrier=1 conns=2 tăcere_max=14s bridge=1 cli=0
2026-08-10T08:52:59+00:00 puls  carrier=1 conns=2 tăcere_max=6s  bridge=1 cli=0
2026-08-10T09:08:07+00:00 puls  carrier=1 conns=2 tăcere_max=13s bridge=1 cli=0
2026-08-10T09:23:15+00:00 puls  carrier=1 conns=2 tăcere_max=5s  bridge=1 cli=0
2026-08-10T09:38:23+00:00 puls  carrier=1 conns=2 tăcere_max=12s bridge=1 cli=0
2026-08-10T09:53:31+00:00 puls  carrier=1 conns=2 tăcere_max=4s  bridge=1 cli=0
2026-08-10T10:08:38+00:00 puls  carrier=1 conns=2 tăcere_max=11s bridge=1 cli=0
2026-08-10T10:23:46+00:00 puls  carrier=1 conns=2 tăcere_max=4s  bridge=1 cli=0
2026-08-10T10:38:54+00:00 puls  carrier=1 conns=2 tăcere_max=10s bridge=1 cli=0
2026-08-10T10:54:02+00:00 puls  carrier=1 conns=2 tăcere_max=4s  bridge=1 cli=0
2026-08-10T11:09:10+00:00 puls  carrier=1 conns=2 tăcere_max=9s  bridge=1 cli=0
2026-08-10T11:24:18+00:00 puls  carrier=1 conns=2 tăcere_max=5s  bridge=1 cli=0

=== app restarted by hand at 11:33 — only then does anything move ===
2026-08-10T11:33:23+00:00 CONN         SSH from client: 2 -> 1
2026-08-10T11:33:23+00:00 BRIDGE       --bridge: 1 -> 0
2026-08-10T11:33:33+00:00 CONN         SSH from client: 1 -> 2
2026-08-10T11:33:33+00:00 BRIDGE       --bridge: 0 -> 1
2026-08-10T11:34:33+00:00 CCD-CLI      ccd-cli: 0 -> 1

</details>

Detecting this from the server side, for anyone else hitting it:

grep '\[Server\]' ~/.claude/remote/run/*/remote-server.log | tail -5

New connection from: @ followed by a long gap = transport came back, app stayed wedged. Repeated Connection closed / New connection pairs instead = a genuine transport problem, i.e. not this.

Steps to Reproduce

I do not have deterministic steps. What I can offer:

  1. Connect the desktop app to a remote host over SSH.
  2. With the session live, touch the remote's ssh service (sudo systemctl reload ssh). This sends SIGHUP to sshd and does not kill existing sessions.
  3. In my case the app dropped its own connection ~79 s later, reconnected successfully within 10 s, and then displayed "Reconnecting" until it was restarted.

It also happens without any such trigger — more often when the app is left open and idle for long stretches. Frequency for me: roughly every couple of days.

Claude Code Version

ccd-cli deployed on the remote: 2.1.222

Platform

Claude Code desktop app (Windows host), remote SSH mode

Operating System

Client: Windows 11. Remote: Ubuntu 24.04.4 LTS, kernel 6.8.0-137-generic, Hyper-V VM. Both machines on the same LAN, no NAT between them.

Additional Information

I still have the per-second instrumentation running on the remote (link state, SSH connection state, lastrcv per connection, and the bridge/CLI process counts). If there is a specific client-side log or state dump that would help pin down what the app is waiting on, I can catch the next occurrence with it.

View original on GitHub ↗