You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
send_text raises on a nonzero exit — BaseTmuxBackend.send_text (adapters/tmux_base.py:328-331)
goes through _tmux → _run(check=True), and TmuxError subclasses MultiplexerError
(tmux_base.py:44, multiplexer.py:42). Three call sites nudge an idle session, and only one of them
guards that:
site
call
guarded
generic.py:676 budget nudge
self.send_text(handle, BUDGET_NUDGE_TEXT)
yes — except MultiplexerError, comment: "a dead/hung window can't take the nudge"
generic.py:782 stall nudge
self.send_text(handle, STALL_NUDGE_TEXT)
no
generic.py:857 generic nudge
self.send_text(handle, NUDGE_TEXT)
no
Nothing upstack catches it either: grep -n "TmuxError\|MuxError" src/bmad_loop/engine.py is empty, and
the three except Exception blocks in generic.py (:1061, :1067, :1125) are in the teardown
path, not the poll loop. So the exception escapes the session loop.
Why it is reachable
A nudge fires precisely when the session has gone quiet, and the window can die inside the same tick.
The code says so itself, in the comment immediately after the unguarded stall nudge
(generic.py:786-789):
Re-probe liveness before finalizing: this return exits the loop, so a hard death (no SessionEnd) in
the gap since the top-of-tick probe would otherwise never be caught.
That re-probe sits on the finalize branch, after the nudge branch has already run. On the tick where
the stall deadline expires and the window died since the top-of-tick probe, the nudge is attempted
against a dead target first.
Platform status
tmux (Linux/macOS): live today.send-keys to an unresolvable target already exits 1:
So the same admission that removes a silent-misroute hazard converts it into an unhandled exception on
two of three sites.
Ask
Give the stall and generic nudge sites the guard the budget site already has, and decide what a failed
nudge means: the budget site treats it as "the window is gone, stop nudging" and continues to the
liveness re-probe, which looks right for all three.
Worth checking in the same pass whether any other MultiplexerError-raising seam call sits unguarded
inside the poll loop — send_text is the one that runs because the session looks dead, so it is the
most exposed, but the audit is cheap while someone is in there.
Found during the psmux pre-3.3.8 bughunt, while checking which adapter tolerance points the upstream
target-validation fix would harden
Summary
send_textraises on a nonzero exit —BaseTmuxBackend.send_text(adapters/tmux_base.py:328-331)goes through
_tmux→_run(check=True), andTmuxErrorsubclassesMultiplexerError(
tmux_base.py:44,multiplexer.py:42). Three call sites nudge an idle session, and only one of themguards that:
generic.py:676budget nudgeself.send_text(handle, BUDGET_NUDGE_TEXT)except MultiplexerError, comment: "a dead/hung window can't take the nudge"generic.py:782stall nudgeself.send_text(handle, STALL_NUDGE_TEXT)generic.py:857generic nudgeself.send_text(handle, NUDGE_TEXT)Nothing upstack catches it either:
grep -n "TmuxError\|MuxError" src/bmad_loop/engine.pyis empty, andthe three
except Exceptionblocks ingeneric.py(:1061,:1067,:1125) are in the teardownpath, not the poll loop. So the exception escapes the session loop.
Why it is reachable
A nudge fires precisely when the session has gone quiet, and the window can die inside the same tick.
The code says so itself, in the comment immediately after the unguarded stall nudge
(
generic.py:786-789):That re-probe sits on the finalize branch, after the nudge branch has already run. On the tick where
the stall deadline expires and the window died since the top-of-tick probe, the nudge is attempted
against a dead target first.
Platform status
send-keysto an unresolvable target already exits 1:crash path that honors the session's artifact.
send-keysto a stale target exits 0 there andthe keystrokes are typed into whichever window is active — kill-pane/rename-window/send-keys/capture-pane with an unresolvable -t target silently operate on the ACTIVE window (rc=0) instead of erroring psmux/psmux#545, which we filed and which
is now fixed on psmux master (
194fac0,send-keysis in the new validator's command list). Themoment 3.3.8 is admitted, psmux joins tmux and this path starts raising.
So the same admission that removes a silent-misroute hazard converts it into an unhandled exception on
two of three sites.
Ask
Give the stall and generic nudge sites the guard the budget site already has, and decide what a failed
nudge means: the budget site treats it as "the window is gone, stop nudging" and continues to the
liveness re-probe, which looks right for all three.
Worth checking in the same pass whether any other
MultiplexerError-raising seam call sits unguardedinside the poll loop —
send_textis the one that runs because the session looks dead, so it is themost exposed, but the audit is cheap while someone is in there.
Found during the psmux pre-3.3.8 bughunt, while checking which adapter tolerance points the upstream
target-validation fix would harden
Refs: psmux/psmux#545, #222