Interview frequency: …
Short answer
When several concurrent operations fail at the same time (such as TaskGroup tasks) and you need to show every error and handle each type, instead of settling for the first error.
Explanation
ExceptionGroup (since Python 3.11) bundles several exceptions into one object. except* handles the matching types inside the group, more than one except* block can run for the same group, and whatever is not handled is automatically re-raised in a new group. TaskGroup always raises an ExceptionGroup when any task fails, so the calling code must know this shape. Do not use it in ordinary sequential code.
Code example
import asyncio
async def main():
try:
async with asyncio.TaskGroup() as tg:
tg.create_task(fetch_users())
tg.create_task(fetch_orders())
except* TimeoutError as group:
log.warning("timeouts: %s", group.exceptions)
except* ValueError as group:
log.error("bad data: %s", group.exceptions)Common mistakes
- Writing except TimeoutError around a TaskGroup, which catches nothing because an ExceptionGroup is what gets raised.
- Mixing except and except* in the same try statement (not allowed).
Follow-up questions
- What is the practical difference between asyncio.gather and TaskGroup?
- How do you choose the right exception and deal with the exception hierarchy?