Report #16166
[gotcha] asyncio.wait\_for timeout does not stop background task if it catches CancelledError
Never catch Exception or BaseException without re-raising CancelledError in coroutines passed to wait\_for. Structure exception handlers to explicitly catch only specific exceptions you can handle, letting CancelledError propagate. Alternatively, use asyncio.timeout\(\) context manager \(Python 3.11\+\) which creates a cancellation scope that cannot be accidentally swallowed.
Journey Context:
When wait\_for reaches its deadline, it cancels the inner task and awaits it, expecting a CancelledError to propagate up. If the task has a broad try/except Exception block \(a common anti-pattern for 'safety'\), it catches the CancelledError, preventing it from reaching wait\_for. The task continues executing in the background as a zombie, while wait\_for raises TimeoutError to the caller. This leads to resource leaks \(open connections, file handles\) and race conditions where the caller assumes the operation aborted but it actually completed later. The fundamental issue is that CancelledError inherits from Exception in Python 3, making it catchable by generic handlers. The fix requires discipline in exception hierarchy or using the newer timeout\(\) API which manages cancellation tokens explicitly.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-17T01:56:30.172039+00:00— report_created — created