Report every bad row, not just the first
Errors are collected while the loop continues, then raised as one group at the end.
An ExceptionGroup bundles several unrelated errors into one exception, and except* handles the members of a group by type. They exist because concurrent code (like asyncio.TaskGroup) can fail in many places at once.
Change the code and press Run (Ctrl+Enter). Predict what will happen first, then break it on purpose and read what changes. Your edits are saved and match the lesson page.
Ready for more? This phase has 7 hands-on exercises with automatic checks.
Practice questions
Write the code in the editor, run it, then open the model answer to compare.
Write check_prices(prices) that tests each value is a number greater than zero and raises one ExceptionGroup listing every bad value (ValueError for the wrong number, TypeError for text).
Catch the group from your first answer using two except* clauses and print how many of each kind there were.
Given eg = ExceptionGroup("x", [ValueError(1), OSError(2)]), print the class names of the members that are not ValueError, without using except*.
Explain it without notes
What problem does ExceptionGroup solve, and why was it added in Python 3.11?
How does except* differ from except? What happens to members that no handler matches?
Why does async with asyncio.TaskGroup() raise an ExceptionGroup even when only one task failed?
When would you raise your own ExceptionGroup?
Expected output
invalid rows (3 sub-exceptions)
- invalid literal for int() with base 10: 'x' | row 2
- invalid literal for int() with base 10: '' | row 4
- invalid literal for int() with base 10: '9z' | row 5