{"record":{"id":"5a4894b3b5453fcc","repo":"Textualize/textual","slug":"request-to-run-a-non-async-function-as-an-async-wo","errorCode":null,"errorMessage":"Request to run a non-async function as an async worker","messagePattern":"Request to run a non-async function as an async worker","errorType":"exception","errorClass":"WorkerError","httpStatus":null,"severity":"error","filePath":"src/textual/worker.py","lineNumber":343,"sourceCode":"        assert loop is not None\n        return await loop.run_in_executor(None, runner, self._work)\n\n    async def _run_async(self) -> ResultType:\n        \"\"\"Run an async worker.\n\n        Returns:\n            Return value of the work.\n        \"\"\"\n        if (\n            inspect.iscoroutinefunction(self._work)\n            or hasattr(self._work, \"func\")\n            and inspect.iscoroutinefunction(self._work.func)\n        ):\n            return await self._work()\n        elif inspect.isawaitable(self._work):\n            return await self._work\n        elif callable(self._work):\n            raise WorkerError(\"Request to run a non-async function as an async worker\")\n        raise WorkerError(\"Unsupported attempt to run an async worker\")\n\n    async def run(self) -> ResultType:\n        \"\"\"Run the work.\n\n        Implement this method in a subclass, or pass a callable to the constructor.\n\n        Returns:\n            Return value of the work.\n        \"\"\"\n        return await (\n            self._run_threaded() if self._thread_worker else self._run_async()\n        )\n\n    async def _run(self, app: App) -> None:\n        \"\"\"Run the worker.\n\n        Args:","sourceCodeStart":325,"sourceCodeEnd":361,"githubUrl":"https://github.com/Textualize/textual/blob/06dbeef4bb70fb718236aa418ed658ef4667a126/src/textual/worker.py#L325-L361","documentation":"WorkerError from Worker._run_async: the worker was started in async mode but the work item is a plain (non-async) callable. Async workers execute coroutines/awaitables only; sync callables must run as thread workers.","triggerScenarios":"`run_worker(plain_function)` with default exclusive/thread settings that route to _run_async (i.e. not thread=True), or calling `worker.run()` on a Worker wrapping a normal function in async mode.","commonSituations":"Forgetting the `thread=True` (or @work(thread=True)) argument when passing a blocking function; refactoring an async worker into a sync function without updating flags.","solutions":["Add thread=True: `self.run_worker(my_sync_fn, thread=True)` or `@work(thread=True)`.","Or make the function async (`async def`) so the async runner can await it."],"exampleFix":"# before\ndef load(): ...\nself.run_worker(load)\n# after\ndef load(): ...\nself.run_worker(load, thread=True)","handlingStrategy":"validation","validationCode":"import inspect\nif callable(fn) and not inspect.iscoroutinefunction(fn):\n    self.run_worker(fn, thread=True)\nelse:\n    self.run_worker(fn)","typeGuard":"import inspect\ndef is_async_callable(fn) -> bool:\n    f = getattr(fn, 'func', fn)\n    return inspect.iscoroutinefunction(f)","tryCatchPattern":"from textual.worker import WorkerError\ntry:\n    await worker.run()\nexcept WorkerError:\n    worker._run_threaded()","preventionTips":["Mark @work methods thread=True when they block","Keep blocking I/O out of async workers"],"tags":["worker","async","thread","textual"],"backgroundTag":"worker-misconfiguration","analyzedSha":"06dbeef4bb70fb718236aa418ed658ef4667a126","analyzedAt":"2026-08-27T02:36:57.214Z","schemaVersion":2},"datasetVersion":"2026-08-27T03:17:27.898Z"}