Textualize/textual · error · RuntimeError

App is not running

Error message

App is not running

What it means

RuntimeError raised by App.call_from_thread when self._loop is None, i.e. the app's asyncio event loop is not (yet/anymore) running. The method marshals a callback onto that loop, so it cannot work without it.

Source

Thrown at src/textual/app.py:1819

        !!! tip

            Consider using [post_message][textual.message_pump.MessagePump.post_message] which is also thread-safe.

        Args:
            callback: A callable to run.
            *args: Arguments to the callback.
            **kwargs: Keyword arguments for the callback.

        Raises:
            RuntimeError: If the app isn't running or if this method is called from the same
                thread where the app is running.

        Returns:
            The result of the callback.
        """

        if self._loop is None:
            raise RuntimeError("App is not running")

        if self._thread_id == threading.get_ident():
            raise RuntimeError(
                "The `call_from_thread` method must run in a different thread from the app"
            )

        callback_with_args = partial(callback, *args, **kwargs)

        async def run_callback() -> CallThreadReturnType:
            """Run the callback, set the result or error on the future."""
            with self._context():
                return await invoke(callback_with_args)

        # Post the message to the main loop
        future: Future[CallThreadReturnType] = asyncio.run_coroutine_threadsafe(
            run_callback(), loop=self._loop
        )
        result = future.result()

View on GitHub (pinned to 06dbeef4bb)

Solutions

  1. Only call call_from_thread once the app is running (from on_mount onward)
  2. Check app._loop / app.is_running before calling, or defer with a small retry/queue
  3. Cancel/suppress late callbacks on shutdown (e.g. check a shutdown flag in the thread)

Example fix

# before
def worker():
    app.call_from_thread(update_ui)  # may run before loop starts

# after
async def on_mount(self):
    self.run_worker(worker, thread=True)

def worker():
    if self.app._loop is not None:
        self.app.call_from_thread(update_ui)
Defensive patterns

Strategy: validation

Validate before calling

if app._loop is not None and app.is_running:
    app.call_from_thread(cb)
else:
    queue_or_skip()

Try / catch

try:
    app.call_from_thread(cb)
except RuntimeError:
    pass  # app shutting down; drop update

Prevention

When it happens

Trigger: Calling call_from_thread from a worker thread before the app finished starting (before on_mount/run), or after the app has shut down; also when called outside run_async/run_terse lifecycle.

Common situations: Thread workers spawned at __init__ time firing before the loop is set; callbacks arriving after user exit; tests driving methods without running the app.

Related errors


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