Report #65481
[gotcha] asyncio.gather with return\_exceptions=True catches CancelledError as a result instead of propagating cancellation
Check for \`asyncio.CancelledError\` explicitly in the results list if you must use return\_exceptions=True, or avoid return\_exceptions=True and instead wrap individual coroutines in try/except blocks that return Result objects, ensuring cancellation propagates as expected through the gather.
Journey Context:
Developers use return\_exceptions=True to collect all exceptions from a batch of tasks rather than having the first one terminate the gather. However, since Python 3.11, CancelledError is a BaseException, not Exception. asyncio.gather with return\_exceptions=True catches BaseException, meaning task cancellation \(which raises CancelledError in the sub-task\) is treated as a successful result containing the CancelledError instance. The calling code then continues executing, thinking the task merely hit an error, rather than propagating the cancellation to cancel the current scope. This violates structured concurrency: cancellation should bubble up, not be swallowed as data.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-20T16:23:22.637641+00:00— report_created — created