docs(automation_patterns): the wait helpers never wait — the marker matches the command echo
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 72/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Quiet
- Tech stack
- python
- Domain
- documentation
Research direction
Start with docs/topics/automation_patterns.md and review the wait_for_output and capture_after_marker examples, then read Server.wait_for() in src/libtmux/server.py. Run the page's doctests or the automation_patterns.md[4] check and add assertions covering delayed output, timeout, and captured command output. Done means the documented helpers no longer return on the echoed command and the examples reliably pass.
Written by the indexing model from the issue text.
Description
Bug
Both wait helpers taught in docs/topics/automation_patterns.md return before the command they are waiting on has produced any output, because the shell echoes the command line and the marker is matched in that echo.
Same root cause as #654, but in the documentation — so it is the pattern readers copy into their own code.
The two helpers fail differently, and the quieter one is the worse one:
| helper | failure |
|---|---|
wait_for_output |
always returns True immediately. timeout and poll_interval are dead parameters. Its doctest passes vacuously — it would pass if the command never ran at all. |
capture_after_marker |
its poll loop exits on the first capture, so timeout=2.0 never waits; the only real wait is a bare time.sleep(0.3). Visibly flaky — automation_patterns.md[4] fails intermittently. |
Root cause
send-keys types the command into the pane, and the shell echoes it back before running it. The echoed line contains the marker string, so a substring match over capture_pane() succeeds instantly.
docs/topics/automation_patterns.md#L137-L139:
>>> monitor_pane.send_keys('sleep 0.2; echo "READY"')
>>> wait_for_output(monitor_pane, 'READY', timeout=2.0)
True
READY is in sleep 0.2; echo "READY" — the command itself. The helper matches the echo, not the output.
Reproduction
The helper verbatim from the page, with the sleep raised so the race is unmissable:
pane.send_keys('sleep 2.0; echo "READY"')
got = wait_for_output(pane, "READY", timeout=5.0) # helper copied from the docs
Observed:
wait_for_output(...) returned True after 0.02s
the command needs >= 2.0s to print READY
screen at that moment:
'sleep 2.0; echo "READY"' <- only the echo; no output yet
Observed: True in 0.02 s.
Expected: blocks ~2 s, then True — or False on timeout.
capture_after_marker fails the same way: its first captured line containing MARKER is index 0, the echoed command.
Suggested fix
Teach tmux wait-for rather than polling for a string. libtmux already wraps it — Server.wait_for() — and it is race-free by construction: no marker, no poll interval, no sleep.
pane.send_keys('sleep 2.0; echo "captured data"; tmux wait-for -S done')
server.wait_for('done') # blocks until the command actually finished
result = pane.capture_pane()
There is no lost-wakeup race even if the command finishes before the wait is issued. From tmux's cmd-wait-for.c:
/* signal: nobody waiting yet -> remember it */
if (TAILQ_EMPTY(&wc->waiters) && !wc->woken) { wc->woken = 1; return (CMD_RETURN_NORMAL); }
/* wait: already woken -> return at once */
if (wc->woken) { cmd_wait_for_remove(wc); return (CMD_RETURN_NORMAL); }
Inside a pane $TMUX is already set, so a bare tmux wait-for -S done reaches the right server with no extra wiring.
If a string-matching example is still wanted — reasonable for output you do not control — it must not be matchable against the echo. Either match per line and skip the echoed command (the fix proposed in #654), or use a sentinel that cannot appear in the command line, e.g. assemble it at runtime so the literal is never typed.
Tests
The page's doctests currently prove nothing, so they need to become real assertions:
wait_for_outputagainst a command that sleeps longer than the poll interval must take at least that long to return — that fails today.wait_for_outputmust returnFalseon timeout when the text never appears (today it cannot, since it matches the echo).capture_after_markermust return the command's output, not the tail of the echoed line.
Related
- #654 — same root cause, in a test rather than the docs
- #484, #456 — adjacent
capture_panereliability
Environment
- libtmux: v0.61.0 (both helpers unchanged since the tag)
- tmux: 3.7b
- Python: 3.14
- Dominant language
- Python
- Stars
- 1.2k
- Forks
- 127
- Avg merge
- 2h 13m
- Merged PRs (30d)
- 1
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from tmux-python/libtmux
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
tmux-python/libtmux#759 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
tmux-python/libtmux#745 · 2 comments ·
-
enhancement
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
tmux-python/libtmux#744 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
tmux-python/libtmux#731 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
tmux-python/libtmux#654 ·
All issues in tmux-python/libtmux
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
enhancement
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 74/100