{"record":{"id":"e330caa4c023b9ca","repo":"Textualize/textual","slug":"app-is-not-running","errorCode":null,"errorMessage":"App is not running","messagePattern":"App is not running","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"src/textual/app.py","lineNumber":1819,"sourceCode":"        !!! tip\n\n            Consider using [post_message][textual.message_pump.MessagePump.post_message] which is also thread-safe.\n\n        Args:\n            callback: A callable to run.\n            *args: Arguments to the callback.\n            **kwargs: Keyword arguments for the callback.\n\n        Raises:\n            RuntimeError: If the app isn't running or if this method is called from the same\n                thread where the app is running.\n\n        Returns:\n            The result of the callback.\n        \"\"\"\n\n        if self._loop is None:\n            raise RuntimeError(\"App is not running\")\n\n        if self._thread_id == threading.get_ident():\n            raise RuntimeError(\n                \"The `call_from_thread` method must run in a different thread from the app\"\n            )\n\n        callback_with_args = partial(callback, *args, **kwargs)\n\n        async def run_callback() -> CallThreadReturnType:\n            \"\"\"Run the callback, set the result or error on the future.\"\"\"\n            with self._context():\n                return await invoke(callback_with_args)\n\n        # Post the message to the main loop\n        future: Future[CallThreadReturnType] = asyncio.run_coroutine_threadsafe(\n            run_callback(), loop=self._loop\n        )\n        result = future.result()","sourceCodeStart":1801,"sourceCodeEnd":1837,"githubUrl":"https://github.com/Textualize/textual/blob/06dbeef4bb70fb718236aa418ed658ef4667a126/src/textual/app.py#L1801-L1837","documentation":"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.","triggerScenarios":"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.","commonSituations":"Thread workers spawned at __init__ time firing before the loop is set; callbacks arriving after user exit; tests driving methods without running the app.","solutions":["Only call call_from_thread once the app is running (from on_mount onward)","Check app._loop / app.is_running before calling, or defer with a small retry/queue","Cancel/suppress late callbacks on shutdown (e.g. check a shutdown flag in the thread)"],"exampleFix":"# before\ndef worker():\n    app.call_from_thread(update_ui)  # may run before loop starts\n\n# after\nasync def on_mount(self):\n    self.run_worker(worker, thread=True)\n\ndef worker():\n    if self.app._loop is not None:\n        self.app.call_from_thread(update_ui)","handlingStrategy":"validation","validationCode":"if app._loop is not None and app.is_running:\n    app.call_from_thread(cb)\nelse:\n    queue_or_skip()","typeGuard":null,"tryCatchPattern":"try:\n    app.call_from_thread(cb)\nexcept RuntimeError:\n    pass  # app shutting down; drop update","preventionTips":["Spawn thread workers only from on_mount onward","Check is_running before posting from threads","Drop stale updates on shutdown"],"tags":["threading","asyncio","lifecycle","textual"],"backgroundTag":"event-loop-not-running","analyzedSha":"06dbeef4bb70fb718236aa418ed658ef4667a126","analyzedAt":"2026-08-27T02:36:57.214Z","schemaVersion":2},"datasetVersion":"2026-08-27T03:17:27.898Z"}