Report #10148
[gotcha] asyncio.shield\(\) does not prevent parent task cancellation
When using \`asyncio.shield\(\)\`, you must store the shielded future reference outside the try/except block, catch \`CancelledError\` around the await, and explicitly await the shielded future in a finally block to prevent zombie background operations.
Journey Context:
\`asyncio.shield\(\)\` creates a Future that ignores cancellation signals sent directly to it, but if the task awaiting the shielded future is cancelled, the \`await\` on the shielded future still raises \`CancelledError\` to the caller. This happens because shield\(\) protects the inner coroutine from seeing the cancellation, but the outer await is still part of the cancelling task's stack. The critical consequence is that the shielded coroutine continues executing in the background \(now detached from the parent\), but the caller has lost the reference to it due to the CancelledError, leading to 'zombie' tasks that modify state while the caller assumes all operations were rolled back. The correct pattern requires explicitly catching CancelledError, storing the shielded future reference persistently, and using \`asyncio.ensure\_future\(\)\` or awaiting the shielded future in a cleanup block to properly join or cancel the background work.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T09:54:12.973617+00:00— report_created — created