ExceptionGroup
PythonERRORCriticalRuntime ErrorHIGH confidence

Multiple exceptions raised simultaneously

Production Risk

Common when using asyncio.TaskGroup; always handle with except* to process each failure.

What this means

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.

Why it happens
  1. 1Multiple async tasks in an asyncio.TaskGroup raise exceptions
  2. 2A test framework collects multiple assertion failures
  3. 3Code explicitly raises ExceptionGroup with multiple sub-exceptions
How to reproduce

Two async tasks both failing inside a TaskGroup.

trigger — this will error
trigger — this will error
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

Use except* to handle specific exception types
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.

Code examples
Triggerpython
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-exceptions
Handle with except*python
try:
    async with asyncio.TaskGroup() as tg:
        tg.create_task(work())
except* ValueError as eg:
    for exc in eg.exceptions:
        print(f"ValueError: {exc}")
Avoid by handling each task individuallypython
async def safe_task(coro):
    try:
        return await coro
    except Exception as e:
        return e  # return error instead of raising
What not to do

Use except ExceptionGroup: to catch all sub-exceptions generically

You lose type information; use except* to handle each exception type individually.

Same error in other languages
Version notes

Content generated with AI assistance and reviewed for accuracy. Found an error? hello@errcodes.dev

← All Python errors