Report #87173
[gotcha] Asyncio task cancellation is silently swallowed or cleanup is skipped
Put cleanup in \`try/finally\`; if you catch \`asyncio.CancelledError\`, re-raise it. Only suppress it intentionally and then call \`task.uncancel\(\)\` so structured concurrency primitives keep working
Journey Context:
When \`task.cancel\(\)\` is called, \`asyncio.CancelledError\` is injected into the coroutine at the next \`await\`. Because \`CancelledError\` subclasses \`BaseException\`, a bare \`except Exception\` will not catch it, but a bare \`except:\` or an explicit \`except CancelledError\` will. Swallowing it prevents the task from recording that it was cancelled and breaks \`asyncio.TaskGroup\`, \`asyncio.timeout\(\)\`, and \`asyncio.gather\(\)\`. Cleanup belongs in \`finally\`, not in the \`except CancelledError\` block, because \`finally\` runs even when cancellation propagates. If you genuinely need to suppress cancellation \(for example to finish a critical flush\), catch and then call \`task.uncancel\(\)\` so the cancellation count is cleared. In nearly all normal code, the right move is to re-raise.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-22T04:54:33.088827+00:00— report_created — created