{"record":{"id":"93467f2e53ab43a4","repo":"PyO3/pyo3","slug":"unexpected-error-in-coroutine-waker","errorCode":null,"errorMessage":"unexpected error in coroutine waker","messagePattern":"unexpected error in coroutine waker","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/coroutine/waker.rs","lineNumber":48,"sourceCode":"        py: Python<'py>,\n    ) -> PyResult<Option<&Bound<'py, PyAny>>> {\n        let init = || LoopAndFuture::new(py).map(Some);\n        let loop_and_future = self.0.get_or_try_init(py, init)?.as_ref();\n        Ok(loop_and_future.map(|LoopAndFuture { future, .. }| future.bind(py)))\n    }\n}\n\nimpl Wake for AsyncioWaker {\n    fn wake(self: Arc<Self>) {\n        self.wake_by_ref()\n    }\n\n    fn wake_by_ref(self: &Arc<Self>) {\n        Python::attach(|py| {\n            if let Some(loop_and_future) = self.0.get_or_init(py, || None) {\n                loop_and_future\n                    .set_result(py)\n                    .expect(\"unexpected error in coroutine waker\");\n            }\n        });\n    }\n}\n\nstruct LoopAndFuture {\n    event_loop: Py<PyAny>,\n    future: Py<PyAny>,\n}\n\nimpl LoopAndFuture {\n    fn new(py: Python<'_>) -> PyResult<Self> {\n        static GET_RUNNING_LOOP: PyOnceLock<Py<PyAny>> = PyOnceLock::new();\n        let import = || -> PyResult<_> {\n            let module = py.import(\"asyncio\")?;\n            Ok(module.getattr(\"get_running_loop\")?.into())\n        };\n        let event_loop = GET_RUNNING_LOOP.get_or_try_init(py, import)?.call0(py)?;","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/PyO3/pyo3/blob/ac9b6899d348be4d54614d060dea53a645a12e36/src/coroutine/waker.rs#L30-L66","documentation":"This panic fires inside a Rust `#[pyclass]` coroutine's `wake_by_ref` when the stored loop-and-future slot reports an error while setting the wake result, i.e. the waker's internal `Once`-style state was initialized to a value that cannot produce a `set_result`. It signals a corrupted or never-properly-initialized coroutine waker rather than a user-facing Python error. The `get_or_init(..., || None)` path stored `None` on first use, so this branch should only run when a loop/future pair actually exists.","triggerScenarios":"Waking an already-completed or dropped coroutine task; the future slot was consumed/cleared between scheduling and wake; calling `wake_by_ref` after the event loop future was already resolved.","commonSituations":"Long-running async Rust functions wrapped with pyo3's coroutine support; cancelling Python `asyncio` tasks whose Rust waker still fires; mixed sync/async shutdown ordering at interpreter teardown.","solutions":["Ensure the coroutine future is not dropped or resolved before its waker is invoked (do not cancel the asyncio task while the Rust side still holds the waker)","Keep the event loop alive for the duration of the coroutine; do not close the loop before awaiting finishes","Upgrade pyo3 — waker state management has been reworked in newer releases","Reproduce with a minimal example and report to PyO3 if the panic occurs in normal awaiting"],"exampleFix":"// before\nlet task = asyncio.create_task(coro); task.cancel(); await asyncio.sleep(0)\n// after\nawait task  # let the coroutine finish before dropping/awaking its waker","handlingStrategy":"try-catch","validationCode":"# Python: ensure the asyncio task/loop is alive before interacting\nassert not task.done(), 'coroutine task already finished; do not wake its waker'","typeGuard":"def is_task_alive(task) -> bool:\n    return task is not None and not task.done() and not task.cancelled()","tryCatchPattern":"try:\n    result = await rust_coroutine(...)\nexcept asyncio.CancelledError:\n    # do not touch the waker afterwards\n    pass","preventionTips":["Await tasks to completion before cancelling or dropping them","Keep the event loop open until all Rust coroutines resolve","Upgrade pyo3 to get fixed waker state handling","Avoid holding Rust wakers beyond the future's completion"],"tags":["rust","async","coroutine","waker"],"backgroundTag":"coroutine-waker-panic","analyzedSha":"ac9b6899d348be4d54614d060dea53a645a12e36","analyzedAt":"2026-09-05T09:20:35.319Z","contentChangedAt":"2026-09-05T09:20:35.319Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}