{"record":{"id":"97692004fc96b4df","repo":"slint-ui/slint","slug":"run-until-complete-s-future-isn-t-done","errorCode":null,"errorMessage":"run_until_complete's future isn't done","messagePattern":"run_until_complete's future isn't done","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"api/python/slint/slint/loop.py","lineNumber":187,"sourceCode":"            asyncio.events._set_running_loop(None)\n\n    def run_until_complete[T](self, future: typing.Awaitable[T]) -> T | None:\n        def stop_loop(future: typing.Any) -> None:\n            self.stop()\n\n        future = asyncio.ensure_future(future, loop=self)\n        future.add_done_callback(stop_loop)\n\n        try:\n            self.run_forever()\n        finally:\n            future.remove_done_callback(stop_loop)\n\n        if future.done():\n            return future.result()\n        else:\n            if self.stop_run_forever_event.is_set():\n                raise RuntimeError(\"run_until_complete's future isn't done\", future)\n            else:\n                # If the loop was quit for example because the user closed the last window, then\n                # don't thrown an error but return a None sentinel. The return value of asyncio.run()\n                # isn't used by slint.run_event_loop() anyway\n                # TODO: see if we can properly cancel the future by calling cancel() and throwing\n                # the task cancellation exception.\n                return None\n\n    def _run_forever_setup(self) -> None:\n        pass\n\n    def _run_forever_cleanup(self) -> None:\n        pass\n\n    def stop(self) -> None:\n        self.stop_run_forever_event.set()\n\n    def is_running(self) -> bool:","sourceCodeStart":169,"sourceCodeEnd":205,"githubUrl":"https://github.com/slint-ui/slint/blob/a9ea814a5813e7460fa1a867924872095a4d678d/api/python/slint/slint/loop.py#L169-L205","documentation":"The Python slint package replaces asyncio's loop with SlintEventLoop, which drives the native slint event loop. In run_until_complete, the future normally completes and stops the loop; if the loop stopped early via loop.stop() (which sets stop_run_forever_event and quits the native loop) while the future is still pending, a RuntimeError mimicking CPython's 'Event loop stopped before Future completed' is raised. Quitting because the last window closed (no stop() call) instead returns a None sentinel without raising.","triggerScenarios":"Running under asyncio.run()/run_until_complete() when something calls loop.stop() (directly, via asyncio.get_running_loop().stop(), a signal handler, or third-party code assuming a plain asyncio loop) before the awaited coroutine finishes.","commonSituations":"Porting asyncio code that ends the loop with loop.stop(); KeyboardInterrupt handlers calling loop.stop(); libraries (aiohttp, websockets, test frameworks) that stop the loop on shutdown while a slint-backed coroutine is still pending.","solutions":["Finish or cancel/await your coroutine before anything stops the loop; structure main() so it returns instead of relying on loop.stop().","Prefer slint.quit_event_loop() (or closing all windows) to end the loop — that path returns the None sentinel instead of raising.","If third-party code insists on loop.stop(), wrap asyncio.run() in try/except RuntimeError and treat the pending future as aborted.","Audit signal handlers and shutdown hooks for loop.stop() calls."],"exampleFix":"# before\nasync def main():\n    await work()\n\nloop = slint.get_event_loop()\nloop.call_later(1, lambda: loop.stop())  # stops early -> RuntimeError\nloop.run_until_complete(main())\n\n# after\nasync def main():\n    await work()\n    slint.quit_event_loop()  # graceful quit, no RuntimeError\n\nslint.run_event_loop_until_quit() or asyncio.run(main())","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    asyncio.run(main())\nexcept RuntimeError as e:\n    if \"future isn't done\" in str(e):\n        # loop.stop() fired before main() finished: treat as aborted run\n        log.warning(\"event loop stopped before main() completed\")\n    else:\n        raise","preventionTips":["Never end the slint loop with loop.stop(); use slint.quit_event_loop() or close all windows.","Make awaited coroutines return on their own instead of relying on external loop stop.","Audit third-party asyncio libraries and signal handlers for loop.stop() calls before adopting them."],"tags":["python","asyncio","event-loop","slint"],"backgroundTag":"event-loop-stopped-before-future-completed","analyzedSha":"a9ea814a5813e7460fa1a867924872095a4d678d","analyzedAt":"2026-08-16T22:39:12.830Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}