fix: stop disowning a healthy client under output bursts - #80
Merged
Conversation
Frames reach an attached client over the reattach socket with a 5s write timeout, and three consecutive timeouts were read as "this client is wedged" and answered with a forced detach. Nothing was wedged. macOS gives a unix socket an 8 KiB buffer where Linux gives ~208 KiB — small enough that a single frame can fill it — so a burst from a chatty session outran the emulator's drain and the server disowned a terminal that was merely behind. Hence the reports of random detaches on a Mac while running omp, and none on Linux. A `SO_SNDTIMEO` expiry also comes back as EAGAIN on Linux but can surface as ETIMEDOUT on BSD/macOS, which `is_transient_terminal_error` did not match. On that path a single timeout skipped the retry budget entirely and detached on the spot. Both ends now request a 512 KiB socket buffer, the server buffers a whole frame before writing and the client drains in 64 KiB reads, ETIMEDOUT is recognized as slowness, and the stall budget is a minute rather than fifteen seconds. Being patient costs nothing here: a client that has actually gone away is still caught immediately by EOF on the relay reader, so this path only ever governs the stalled-but-connected case. Diagnosed from the code — not reproduced on macOS. Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reported as: on macOS, running omp would randomly detach the viewing session.
It is neither random nor omp-specific — it is the relay stall detector firing on a healthy client, and macOS makes it easy to trip.
Mechanism
Frames reach an attached client over the reattach socket with a 5s write timeout. Three consecutive timeouts were read as "this client is wedged" and answered with a forced
AppEvent::Detach(src/main.rs:385-405). Nothing was wedged:net.local.stream.sendspace) where Linux gives ~208 KiB — small enough that a single ratatui frame on a wide terminal can fill it.BufWriterwas the default 8 KiB and the client read in 4 KiB chunks, so one frame crossed the socket as many small blocking writes, each able to stall separately.Separately, a
SO_SNDTIMEOexpiry surfaces asEAGAINon Linux but can come back asETIMEDOUTon BSD/macOS, whichis_transient_terminal_errordid not match. On that path a single timeout skipped the retry budget entirely and detached on the spot.Changes
reattach::widen_socket_buffers— request 512 KiBSO_SNDBUF/SO_RCVBUFon both ends (best-effort; the kernel clamps tokern.ipc.maxsockbuf).BufWriter; client drains in 64 KiB reads.ETIMEDOUT/TimedOutcounted as transient slowness rather than a dead client.3→RELAY_STALL_LIMIT = 12(~60s). Safe: a client that has actually gone away is caught immediately by EOF on the relay reader, so this path only governs the stalled-but-connected case.Testing
cargo clippyclean,cargo fmtapplied, 402 tests pass.Caveat worth a reviewer's attention: this was diagnosed from the code and not reproduced on macOS. The
ETIMEDOUTgap is unambiguously a bug; the buffer sizing is the part that explains the "random" character. If detaches persist, the thing to check is whether they correlate with output bursts at all — if they happen while idle, the cause is elsewhere and the relay reader dropping the connection is the next suspect.🤖 Generated with Claude Code