Textualize/textual · error · WaitForScreenTimeout
Timed out while waiting for widgets to process pending messa
Error message
Timed out while waiting for widgets to process pending messages.
What it means
Thrown when Textual's test Pilot times out waiting for all widgets on a screen to drain their pending message queues. It indicates some widget's message handler is blocked, long-running, or stuck.
Source
Thrown at src/textual/pilot.py:524
if count:
# Wait for the count to return to zero, or a timeout, or an exception
wait_for = [
asyncio.create_task(count_zero_event.wait()),
asyncio.create_task(self.app._exception_event.wait()),
]
_, pending = await asyncio.wait(
wait_for,
timeout=timeout,
return_when=asyncio.FIRST_COMPLETED,
)
for task in pending:
task.cancel()
timed_out = len(wait_for) == len(pending)
if timed_out:
raise WaitForScreenTimeout(
"Timed out while waiting for widgets to process pending messages."
)
# We've either timed out, encountered an exception, or we've finished
# decrementing all the counters (all events processed in children).
if count > 0:
return False
return True
async def pause(self, delay: float | None = None) -> None:
"""Insert a pause.
Args:
delay: Seconds to pause, or None to wait for cpu idle.
"""
# These sleep zeros, are to force asyncio to give up a time-slice.
await self._wait_for_screen()View on GitHub (pinned to 06dbeef4bb)
Solutions
- Find the blocking await in a message handler and offload it with self.run_worker(...)
- Add timeouts to any awaits inside handlers
- Increase test timeout passed to run_test(timeout=...) if processing is legitimately slow
Example fix
# before
async def on_click(self, event) -> None:
await self.load_everything() # blocks message pump
# after
def on_click(self, event) -> None:
self.run_worker(self.load_everything()) Defensive patterns
Strategy: retry
Try / catch
try:
await pilot.pause()
except WaitForScreenTimeout:
pytest.fail("a handler blocked the message pump; check workers/awaits") Prevention
- Never block message handlers with unbounded awaits
- Offload long work to workers
- Set realistic run_test timeouts
When it happens
Trigger: A message handler (on_*, watchers, actions) awaited something that never completes (e.g. await on an unresolved future, infinite loop, or a deadlock) while run_test(), press(), pause(), or wait_for_scheduled_animations() waits for idle.
Common situations: An event handler awaiting network I/O with no timeout, a worker started but awaited inline in a handler, or a modal waiting forever during automated tests.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Can not create a worker from a non-async function unless `th
- unable to run callback {event.callback!r}; {error}
- Target offset is outside of currently-visible screen region.
- RLock.release called too many times
- Can't await screen.dismiss() from the screen's message handl
AI-assisted analysis of Textualize/textual@06dbeef4bb (2026-08-27).
Data as JSON: /api/errors/f45b4d6a902f8200.
Report an issue: GitHub.