Skip to content

fix(client): handle remote terminal hangup (backport #2827) - #5

Open
dhh wants to merge 1 commit into
omarchyfrom
fix/client-remote-terminal-hangup
Open

fix(client): handle remote terminal hangup (backport #2827)#5
dhh wants to merge 1 commit into
omarchyfrom
fix/client-remote-terminal-hangup

Conversation

@dhh

@dhh dhh commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

Cherry-picks upstream herdrdev/herdr#2827 (b660cad, merged 2026-08-15) onto omarchy, which is still at 0766aa57 (2026-08-10) and ships without it.

Why

herdr client aborts with SIGABRT and dumps core when the outer terminal goes away. On one Omarchy machine, coredumpctl has 15 herdr cores; 8 of them are on the currently packaged build.

The packaged build is the problem: 0766aa57 does contain the earlier fix for herdrdev#2424 (#2427, ca1af383, let _ = ratatui::try_restore() at src/client/mod.rs:609), but it does not contain b660cad, so src/client/mod.rs:1329 is still:

eprintln!("herdr: {err}");

Evidence

Every one of the 15 cores has fds 0, 1 and 2 on a deleted PTY — the terminal was already gone when the process wrote to stderr:

2:/dev/pts/13 (deleted)

The 8 cores on the packaged r13 build all abort at the identical offset herdr + 0x112a01a, which disassembles to the call shim into libc abort:

Time (UTC) PID stderr
2026-08-11 15:14:23 3565371 /dev/pts/23 (deleted)
2026-08-11 15:14:23 3565259 /dev/pts/21 (deleted)
2026-08-14 11:36:12 17984 /dev/pts/22 (deleted)
2026-08-14 11:36:13 17771 /dev/pts/21 (deleted)
2026-08-15 19:55:09 3797487 /dev/pts/20 (deleted)
2026-08-15 19:55:23 3310244 /dev/pts/17 (deleted)
2026-08-15 20:11:20 3842742 /dev/pts/12 (deleted)
2026-08-17 10:28:22 273819 /dev/pts/13 (deleted)

(The other 7 cores are the same failure on older builds, at that build's own abort offset.)

The panic payload recovered from the core is:

failed printing to stderr: Input/output error (os error 5)

which is the panic! inside std's print_to (library/std/src/io/stdio.rs) — i.e. an eprint!-family macro whose write returned EIO. The binary's fatal message thread panicked while processing panic. aborting. is reached, and the five-frame Rust panic block at herdr + 0x1108b52 appears twice in the crashing stack, which is the double panic.

Sequence

  1. The client leaves its loop with ClientError::ConnectionLost when the transport ends.
  2. eprintln!("herdr: {err}") writes the final diagnostic to a stderr whose PTY master is gone. write() returns EIO, so the macro panics.
  3. The panic hook chains onward into the terminal-restore path, which reports its own failure to the same dead stderr and panics again.
  4. A panic raised while a panic is in flight is unrecoverable, so std aborts.

SIGABRT, core dumped, and a desktop crash notification each time.

What this changes

Straight cherry-pick of b660cad:

  • restore_terminal_state returns io::Result<()> instead of discarding errors.
  • TerminalGuard gains a restored flag and a consuming restore(), so Drop does not restore twice.
  • The final diagnostic becomes let _ = writeln!(io::stderr(), "herdr: {err}"), which cannot panic.
  • Connection loss during a failed terminal restore is treated as a clean exit.
  • Includes the upstream regression tests in tests/client_mode.rs.

The only conflict was docs/next/CHANGELOG.md, where upstream's ### Fixed section carries entries for other commits not in this backport. Resolved by keeping only the #2424 line.

Verification

  • cargo check --all-targets passes on this branch (ZIG pointed at zig 0.15.2, matching the PKGBUILD).
  • Regression coverage ships with the commit (client_exits_cleanly_when_terminal_hangs_up and neighbours).

Not verified here: a runtime reproduction against a rebuilt binary. The reasoning above is from the cores plus the packaged source, not from observing the patched build survive a hangup.

Follow-up

pkgbuilds/herdr/PKGBUILD in omacom-io/omarchy-pkgs pins _commit=0766aa57. It needs bumping past this commit before the packaged build picks the fix up.


Opened on behalf of @dhh, from cores on his machine. Prepared by Claude Fable 5 via Claude Code.

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.

2 participants