Report #104361
[gotcha] asyncio.CancelledError inherits from BaseException, not Exception, so except Exception does not catch it; failing to re-raise it breaks task cancellation
Catch \`asyncio.CancelledError\` explicitly and always re-raise it unless you intend to suppress cancellation. Prefer \`finally\` blocks for cleanup that must run even on cancellation. Use \`asyncio.shield\(\)\` to protect a section from cancellation.
Journey Context:
When a task is cancelled, a \`CancelledError\` is raised inside the coroutine. Since it inherits from \`BaseException\` \(not \`Exception\`\), a bare \`except Exception\` will silently fail to catch it, and then the error propagates. Worse, some code catches \`BaseException\` and swallows it, preventing cancellation from ever completing — causing tasks to hang forever. The correct pattern is to either let \`CancelledError\` propagate \(often by omitting an explicit except\) or to catch it, perform minimal cleanup, and re-raise. \`asyncio.shield\(\)\` can protect critical sections by delaying cancellation until after the shielded block completes. This is documented but frequently missed in production async code.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-08-09T20:02:42.299355+00:00— report_created — created