Textualize/textual · error · RuntimeError

The `call_from_thread` method must run in a different thread

Error message

The `call_from_thread` method must run in a different thread from the app

What it means

RuntimeError raised by App.call_from_thread when executed on the app's own thread (thread ids match). The API exists specifically to hop from a worker thread onto the event-loop thread; calling it from the loop thread is a misuse.

Source

Thrown at src/textual/app.py:1822

        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()
        return result

    def action_change_theme(self) -> None:

View on GitHub (pinned to 06dbeef4bb)

Solutions

  1. From async context just call the function directly, or await an asyncio helper
  2. Restrict call_from_thread to @work(thread=True) worker bodies
  3. In shared helpers, branch on app._thread_id == threading.get_ident()

Example fix

# before
async def on_click(self):
    self.app.call_from_thread(self.update_ui)  # same thread → error

# after
async def on_click(self):
    self.update_ui()
Defensive patterns

Strategy: validation

Validate before calling

import threading
if threading.get_ident() != app._thread_id:
    app.call_from_thread(cb)
else:
    cb()  # already on the loop thread

Prevention

When it happens

Trigger: Invoking app.call_from_thread(...) inside an async method, on_mount, or any code already running on the app thread.

Common situations: Shared helper functions used both from async handlers and thread workers; copy-pasted thread-updating code into normal event handlers.

Related errors


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