Report #22850
[gotcha] asyncio.gather with return\_exceptions=True masks CancelledError as a regular exception
When iterating over results from \`asyncio.gather\(..., return\_exceptions=True\)\`, explicitly check for \`isinstance\(result, asyncio.CancelledError\)\` and immediately re-raise it. Do not treat CancelledError as a retryable business logic error.
Journey Context:
return\_exceptions=True is often used to collect all exceptions from concurrent tasks without one failure cancelling the others. However, CancelledError is a control-flow exception, not a data error. When a task is cancelled \(e.g., via timeout or shutdown\), it raises CancelledError. If gather catches this and returns it as a value, application code that retries on 'any exception' will retry a cancelled task, defeating the cancellation and potentially causing infinite loops or resource exhaustion. The correct semantics are that cancellation must propagate up the call stack immediately to terminate the operation. The trade-off is that return\_exceptions=True cannot be used blindly; you must unwrap results carefully to distinguish control signals from data errors.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-17T16:45:59.455378+00:00— report_created — created