Skip to content

fix(ssh): stop reconnecting after the remote shell exits on purpose - #183

Merged
kipavy merged 1 commit into
devfrom
fix/ssh-exit-no-reconnect-180
Aug 26, 2026
Merged

fix(ssh): stop reconnecting after the remote shell exits on purpose#183
kipavy merged 1 commit into
devfrom
fix/ssh-exit-no-reconnect-180

Conversation

@kipavy

@kipavy kipavy commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Fixes #180.

The bug

With Settings → Hosts → Persistent sessions off, typing exit in an SSH terminal disconnected and then immediately reconnected to the same host. With persistent sessions on, exit behaved correctly.

Root cause

ssh-closed-<id> carried no reason for the close, so handleSessionClosed treated a deliberate exit exactly like a dropped link and started the reconnect backoff. Persistent sessions escaped it only by accident: their attach-only probe fails with SESSION_ENDED, which tears the tab down.

A deliberate exit and a dropped link are distinguishable on the wire. Verified against a local sshd:

  • exit from an interactive shell → client_input_channel_req: channel 0 rtype exit-status
  • transport killed mid-session → zero exit-status messages

The fix

  • channel_io.rs records an ExitStatus / ExitSignal seen before Eof/Close and ships that flag as the ssh-closed-<id> payload.
  • handleSessionClosed ends the session (disconnect + drop the tab, via closeSession) instead of reconnecting when the flag is set — matching what persistent sessions already do when their session ends on the host.
  • Persistent sessions keep the old path: their tmux/screen wrapper also exits on a detach, so the attach probe stays the judge there.
  • The three terminal views (MainPanel, PaneTerminal, MobileSessionView) each built the same deps object; they now share one sessionClosed helper.

Tests

  • channel_io::tests::only_a_reported_exit_counts_as_a_remote_exit — exit-status/exit-signal count, Eof/Close/other messages do not.
  • reconnectBackoff.test.ts — clean exit ends the session; clean exit on a persistent session still reconnects; a drop with no exit-status still reconnects.
  • tsc --noEmit clean.

Live run

Headless container on this branch, persistent sessions toggled off, real SSH host (ssh-host-1):

  • Typed exit → the connected session left the store within 1 s and stayed gone for the 13 s watched; its tab disappeared from the tab bar. No reconnect.
  • Control, same build: kill -9 on the remote sshd-session pids (a drop, no exit-status) → session went connecting at 2 s and back to connected at 4 s. Reconnect still works.

With persistent sessions off, typing `exit` closed the channel and the
frontend immediately reconnected, resurrecting the session the user had
just ended (#180). Persistent sessions escaped it only by accident: their
attach probe fails with SESSION_ENDED and tears the tab down.

The close event carried no reason, so a deliberate exit and a dropped link
looked identical. They are not: the far side sends an exit-status (or
exit-signal) before closing a command that ran to completion, and sends
neither when the link dies. Carry that flag on `ssh-closed-<id>` and end
the session instead of reconnecting when it is set.

Persistent sessions keep the old path: their tmux/screen wrapper also
exits on a detach, so the attach probe stays the judge there.

The three terminal views built the same `handleSessionClosed` deps object;
they now share one `sessionClosed` helper.
@kipavy
kipavy merged commit 0f6ad87 into dev Aug 26, 2026
6 of 7 checks passed
@kipavy
kipavy deleted the fix/ssh-exit-no-reconnect-180 branch August 26, 2026 00:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant