Report #9926
[gotcha] asyncio.shield raises CancelledError immediately when parent task cancelled even though inner task continues
Wrap shield\(\) in its own try/except CancelledError block if you need to differentiate parent cancellation from inner task failure, or structure code so shielded operations don't hold critical cleanup logic that must complete before the parent exits. Do not assume shield\(\) blocks the CancelledError from reaching your current stack frame.
Journey Context:
Developers intuitively expect shield\(\) to 'block' cancellation entirely, making the operation atomic from the caller's perspective. However, shield\(\) creates a boundary where the inner task is protected, but the shield\(\) Future itself obeys the parent's cancellation scope. When the parent task is cancelled, shield\(\) immediately raises CancelledError into the parent's execution frame, even while the shielded inner task continues running in the background. This leads to orphaned tasks and inconsistent state where the caller assumes the operation failed and retries, while the original shielded operation modifies shared state concurrently. The confusion stems from conflating 'preventing cancellation of the operation' with 'preventing the cancellation exception from propagating to my current stack frame'.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T09:22:38.848290+00:00— report_created — created