Report #100604
[gotcha] Catching \`asyncio.CancelledError\` and not re-raising breaks structured concurrency and leaves tasks in an inconsistent state
Only catch \`CancelledError\` to run cleanup; always re-raise it. For timeouts, catch \`TimeoutError\`, not \`CancelledError\`. Use \`asyncio.shield\(\)\` when a sub-operation must survive the caller's cancellation.
Journey Context:
\`CancelledError\` subclasses \`BaseException\`, not \`Exception\`, because cancellation is a control signal, not an application error. Swallowing it prevents \`asyncio.TaskGroup\`, \`asyncio.timeout\(\)\`, and \`asyncio.wait\_for\(\)\` from working correctly; the task may appear cancelled but its parent may wait forever or leak state. Cleanup should go in \`finally\` blocks, and \`CancelledError\` should be re-raised. \`asyncio.shield\(\)\` is the documented tool for protecting a sub-task from the caller's cancellation. In most code you should handle \`TimeoutError\` \(the user-facing wrapper\) and let \`CancelledError\` propagate.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-07-02T04:47:17.556248+00:00— report_created — created