Multiple exceptions raised simultaneously
Production Risk
Common when using asyncio.TaskGroup; always handle with except* to process each failure.
Introduced in Python 3.11, ExceptionGroup allows grouping and raising multiple unrelated exceptions simultaneously. It is primarily used by asyncio.TaskGroup and anyio when multiple concurrent tasks fail.
- 1Multiple async tasks in an asyncio.TaskGroup raise exceptions
- 2A test framework collects multiple assertion failures
- 3Code explicitly raises ExceptionGroup with multiple sub-exceptions
Two async tasks both failing inside a TaskGroup.
import asyncio
async def failing_task(name):
raise ValueError(f"Task {name} failed")
async def main():
async with asyncio.TaskGroup() as tg:
tg.create_task(failing_task("A"))
tg.create_task(failing_task("B"))
asyncio.run(main())expected output
ExceptionGroup: unhandled errors in a TaskGroup (2 sub-exceptions) + Exception Group Traceback (most recent call last): | ValueError: Task A failed + Exception Group Traceback (most recent call last): | ValueError: Task B failed
Fix
Use except* to handle specific exception types
WHEN Catching exceptions from TaskGroup or ExceptionGroup
import asyncio
async def main():
try:
async with asyncio.TaskGroup() as tg:
tg.create_task(failing_task("A"))
tg.create_task(failing_task("B"))
except* ValueError as eg:
for exc in eg.exceptions:
print(f"Caught ValueError: {exc}")
except* IOError as eg:
# Handle I/O errors separately
pass
asyncio.run(main())Why this works
except* (Python 3.11+) matches exception groups by type, giving access to all matching sub-exceptions.
import asyncio
async def fail(n): raise ValueError(f"Task {n} failed")
async def main():
async with asyncio.TaskGroup() as tg:
tg.create_task(fail(1))
tg.create_task(fail(2))
asyncio.run(main()) # ExceptionGroup with 2 sub-exceptionstry:
async with asyncio.TaskGroup() as tg:
tg.create_task(work())
except* ValueError as eg:
for exc in eg.exceptions:
print(f"ValueError: {exc}")async def safe_task(coro):
try:
return await coro
except Exception as e:
return e # return error instead of raising✕ Use except ExceptionGroup: to catch all sub-exceptions generically
You lose type information; use except* to handle each exception type individually.
Content generated with AI assistance and reviewed for accuracy. Found an error? hello@errcodes.dev