Report #38089
[gotcha] asyncio.wait\_for suppresses CancelledError and leaves tasks running as zombies after timeout
Always re-raise \`asyncio.CancelledError\` in the finally block of your task's coroutine; or use \`asyncio.timeout\(\)\` \(3.11\+\) / explicit task management and check \`task.done\(\)\` before assuming cleanup. For 3.10 and below, avoid \`wait\_for\` for critical cancellation semantics without explicit task tracking.
Journey Context:
When \`wait\_for\(\)\` expires, it cancels the wrapped task via \`task.cancel\(\)\`. If the task's coroutine catches \`CancelledError\` \(common in cleanup blocks, resource release, or poorly written exception handlers\) and suppresses it, \`wait\_for\` raises \`TimeoutError\` to the caller, but the task continues running in the background detached from the caller's context. This 'zombie task' phenomenon causes silent resource leaks and logic errors because the caller assumes the task stopped and may restart it or release resources it holds. Python 3.11 introduced \`asyncio.timeout\(\)\` which uses a different cancellation propagation mechanism that is harder to suppress accidentally, but the core gotcha remains in legacy codebases using \`wait\_for\`.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-18T18:24:48.035753+00:00— report_created — created