← back to Terminal Status
terminal-status: retry transient EAGAIN in write_terminal so a busy tty can't latch a 24h health FAIL
1286106d060867af4459e5c603a6cd4463d1909c · 2026-09-15 12:44:34 -0700 · Steve Abrams
write_terminal opened the tty O_NONBLOCK then did a single os.write with no EAGAIN
handling, so a momentarily-full tty buffer raised BlockingIOError (Errno 35) that
escaped to the top handler; dot-floor.sh then latched the fleet-health panel FAIL
for 24h off a ~1.4% transient. Now: bounded 0.5s retry loop handling EAGAIN + partial
writes, raising the already-benign 'Terminal busy; retry' only on a genuine >0.5s wedge.
Verified: py_compile OK + 3 behavioral tests (real-pty positive, 2xEAGAIN+partial, wedged=0.51s bounded).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A5jmQ4a4VhZNPTRkVUSeML
Files touched
Diff
commit 1286106d060867af4459e5c603a6cd4463d1909c
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Sep 15 12:44:34 2026 -0700
terminal-status: retry transient EAGAIN in write_terminal so a busy tty can't latch a 24h health FAIL
write_terminal opened the tty O_NONBLOCK then did a single os.write with no EAGAIN
handling, so a momentarily-full tty buffer raised BlockingIOError (Errno 35) that
escaped to the top handler; dot-floor.sh then latched the fleet-health panel FAIL
for 24h off a ~1.4% transient. Now: bounded 0.5s retry loop handling EAGAIN + partial
writes, raising the already-benign 'Terminal busy; retry' only on a genuine >0.5s wedge.
Verified: py_compile OK + 3 behavioral tests (real-pty positive, 2xEAGAIN+partial, wedged=0.51s bounded).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A5jmQ4a4VhZNPTRkVUSeML
---
terminal_status.py | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/terminal_status.py b/terminal_status.py
index e92291d..b11e54a 100644
--- a/terminal_status.py
+++ b/terminal_status.py
@@ -469,8 +469,24 @@ def write_terminal(owner, payload):
if not stat.S_ISCHR(os.fstat(fd).st_mode) or not os.isatty(fd):
raise StatusError("Paint target is not a terminal")
# One write, no background loop, no terminal input injection.
- if os.write(fd, payload) != len(payload):
- raise StatusError("Incomplete terminal paint")
+ # Non-blocking write: a momentarily busy tty (input buffer full, reader not
+ # draining) makes os.write raise BlockingIOError (EAGAIN / Errno 35) or return
+ # a PARTIAL count. Retry the remaining bytes with a short bounded backoff rather
+ # than turning a transient full buffer into a hard paint failure — an unretried
+ # EAGAIN latched a 24h FAIL on the fleet-health panel (~1.4% of paints hit it).
+ # Keep O_NONBLOCK so a dead/gone reader can never hang us; bound the wait instead.
+ # On genuine wedge (>0.5s) raise "Terminal busy; retry" — the same transient class
+ # dot-floor.sh already treats as benign, not a false engine fault.
+ view = memoryview(payload)
+ sent = 0
+ deadline = time.monotonic() + 0.5
+ while sent < len(view):
+ try:
+ sent += os.write(fd, view[sent:])
+ except BlockingIOError:
+ if time.monotonic() >= deadline:
+ raise StatusError("Terminal busy; retry")
+ time.sleep(0.01)
finally:
os.close(fd)
← 9731e7f TK-11666: measure the TK-REQUIRED false-positive rate (the m
·
back to Terminal Status
·
T2 (TK-11779): add dead-tty .dot/.json reaper (ps-liveness a df6f28c →