{"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/bb937076de3f7919766c1f25e2e969367cf77e9a/api/python/slint/slint/loop.py#L169-L205","documentation":"Raised by run_until_complete when the event loop stopped before the awaited future completed AND the stop was not initiated by the loop's own quit mechanism (stop_run_forever_event set). The result value is therefore unavailable.","triggerScenarios":"Calling loop.run_until_complete(future) while another thread or code path stops the loop (e.g. slint.quit_event_loop()) before the future resolves; nesting run_until_complete calls; an exception path that terminates the loop early.","commonSituations":"Quitting the app from a UI callback while an async task awaited via run_until_complete is still pending; running the Slint loop and an asyncio future concurrently and stopping the loop from a window-close handler.","solutions":["Ensure the future is actually awaited/completed before any code calls quit_event_loop() or stops the loop.","Don't call run_until_complete with futures whose completion depends on UI events that may never fire (window closed, etc.).","Wrap in try/except RuntimeError and handle the not-done case (e.g. cancel the future or treat the result as None)."],"exampleFix":"// before\nresult = slint.loop.run_until_complete(some_coro)  # may raise if loop stopped\n\n# after\ntry:\n    result = slint.loop.run_until_complete(some_coro)\nexcept RuntimeError:\n    some_task.cancel()\n    result = None","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"def future_done(f):\n    return f.done()","tryCatchPattern":"try:\n    result = loop.run_until_complete(fut)\nexcept RuntimeError as e:\n    if \"isn't done\" in str(e):\n        fut.cancel()\n        result = None\n    else:\n        raise","preventionTips":["Never call quit_event_loop() while awaited futures are pending.","Drive long-running async work with ensure_future + the Slint loop's quit callback instead of run_until_complete."],"tags":["python","asyncio","event-loop","race-condition"],"backgroundTag":"invalid-state-transition","analyzedSha":"bb937076de3f7919766c1f25e2e969367cf77e9a","analyzedAt":"2026-09-16T01:37:20.251Z","contentChangedAt":"2026-09-16T01:37:20.251Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}