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

  1. Find the blocking await in a message handler and offload it with self.run_worker(...)
  2. Add timeouts to any awaits inside handlers
  3. 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

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

Related errors


AI-assisted analysis of Textualize/textual@06dbeef4bb (2026-08-27). Data as JSON: /api/errors/f45b4d6a902f8200. Report an issue: GitHub.