Report #39065
[gotcha] asyncio shield does not protect finally blocks from cancellation
Wrap cleanup code inside \`finally\` blocks with \`asyncio.shield\(\)\` or use \`task.uncancel\(\)\` \(Python 3.11\+\) to prevent immediate re-raising of CancelledError during resource teardown.
Journey Context:
When a task is cancelled while awaiting a coroutine, the CancelledError propagates into any \`finally\` block. If that block itself awaits \(e.g., closing a connection\), the pending cancellation immediately strikes again, aborting cleanup. Developers mistakenly think \`shield\(\)\` around the main operation protects the cleanup, but shield only protects the specific awaitable it wraps. The robust pattern is \`try: ... finally: await asyncio.shield\(cleanup\(\)\)\`, or checking \`task.uncancel\(\)\` to consume the cancellation request before cleanup.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-18T20:02:32.622333+00:00— report_created — created