{"record":{"id":"6bfe916326503203","repo":"python/cpython","slug":"await-wasn-t-used-with-future","errorCode":null,"errorMessage":"await wasn't used with future","messagePattern":"await wasn't used with future","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"Lib/asyncio/futures.py","lineNumber":301,"sourceCode":"        if isinstance(exception, StopIteration):\n            new_exc = RuntimeError(\"StopIteration interacts badly with \"\n                                   \"generators and cannot be raised into a \"\n                                   \"Future\")\n            new_exc.__cause__ = exception\n            new_exc.__context__ = exception\n            exception = new_exc\n        self._exception = exception\n        self._exception_tb = exception.__traceback__\n        self._state = _FINISHED\n        self.__schedule_callbacks()\n        self.__log_traceback = True\n\n    def __await__(self):\n        if not self.done():\n            self._asyncio_future_blocking = True\n            yield self  # This tells Task to wait for completion.\n        if not self.done():\n            raise RuntimeError(\"await wasn't used with future\")\n        return self.result()  # May raise too.\n\n    __iter__ = __await__  # make compatible with 'yield from'.\n\n\n# Needed for testing purposes.\n_PyFuture = Future\n\n\ndef _get_loop(fut):\n    # Tries to call Future.get_loop() if it's available.\n    # Otherwise fallbacks to using the old '_loop' property.\n    try:\n        get_loop = fut.get_loop\n    except AttributeError:\n        pass\n    else:\n        return get_loop()","sourceCodeStart":283,"sourceCodeEnd":319,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/asyncio/futures.py#L283-L319","documentation":"Future.__await__ yields the future once so the event loop can resume the waiter when it completes; after resuming, done() must be True. If the coro was driven by something that did not actually await (bare next() on __iter__, custom drivers that ignore the yielded future), the future is still pending and this RuntimeError fires. It means 'you iterated a future without a real awaiter'.","triggerScenarios":"Manually driving a coroutine that awaits a future with next(coro.__await__()) outside a loop; using 'yield from future' inside a generator consumed by non-asyncio machinery; a Task implementation that resumes the coroutine without completing the future.","commonSituations":"Custom task/trampoline implementations; calling coro.send(None) once in tests; mixing curio/trio-style drivers with asyncio futures; debugging tools stepping through coroutines manually.","solutions":["Always consume futures with await inside a real asyncio Task (asyncio.run, loop.create_task)","In custom drivers, after the future completes, resume the coroutine with send/close properly","Replace manual iteration with await asyncio.wait([fut])"],"exampleFix":"# before\ncoro = future.__await__()\nnext(coro)  # manual drive -> RuntimeError on next iteration\n\n# after\nresult = await future  # inside a coroutine run by asyncio","handlingStrategy":"validation","validationCode":"# ensure the awaiter runs inside a real task\nassert asyncio.current_task() is not None","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never drive coroutines with raw next()/send() outside asyncio","Use await exclusively inside tasks created by the loop","Test custom drivers against both pending and completed futures"],"tags":["asyncio","future","await","coroutine"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}