Report #7660
[gotcha] asyncio.wait\_for\(\) returns after timeout but task continues running if it catches CancelledError
Use asyncio.timeout\(\) \(Python 3.11\+\) instead of wait\_for\(\), or ensure the wrapped task never catches CancelledError without re-raising it. If using wait\_for\(\), wrap the task in a shielded cancel scope or accept that cancellation is cooperative and may be ignored.
Journey Context:
asyncio.wait\_for\(\) works by wrapping the coroutine in a Task and then cancelling that task when the timeout expires. However, if the Task's coroutine catches asyncio.CancelledError and does not re-raise it \(common in cleanup blocks or poorly written exception handlers\), the Task continues running in the background even after wait\_for\(\) has returned control to the caller. This creates a 'zombie' task that the caller assumes is dead. Python 3.11 introduced asyncio.timeout\(\) which uses a different mechanism \(cancel scopes\) that avoids this specific issue. The fundamental lesson is that cancellation in asyncio is cooperative; a timeout does not guarantee task termination unless the task respects cancellation.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T03:20:56.884578+00:00— report_created — created