🎓 A1Learner
🔍

Python Exception Groups

An exception group is one exception that bundles several exceptions together. The group reports every failure at once.

Build a group

A group has a name and a list of errors. Its text form reports how many errors it holds.

errors = [ValueError("Name is empty"), TypeError("Age must be a number")]
group = ExceptionGroup("form problems", errors)
print(str(group))
print(len(group.exceptions))

Raise and catch a group

A group travels like any exception. A plain except catches the whole bundle at once.

try:
    raise ExceptionGroup("checks", [ValueError("Bad name"), ValueError("Bad city")])
except ExceptionGroup as group:
    print(len(group.exceptions))
    for err in group.exceptions:
        print(err)

Catch one type with except*

Each except* block takes only its own type. The rest of the group moves to the next block.

try:
    raise ExceptionGroup("form", [ValueError("Name is empty"), TypeError("Age is text"), ValueError("City is empty")])
except* ValueError as group:
    print("Value errors:", len(group.exceptions))
except* TypeError as group:
    print("Type errors:", len(group.exceptions))

Leftovers keep travelling

An except* block leaves other types behind. The leftovers form a smaller group, raised again.

try:
    try:
        raise ExceptionGroup("form", [ValueError("Name is empty"), TypeError("Age is text")])
    except* ValueError as group:
        print("Handled:", len(group.exceptions))
except ExceptionGroup as group:
    print("Left over:", len(group.exceptions))
    print(type(group.exceptions[0]).__name__)

Example

def check_form(name, age, email, visits):
    errors = []
    if name == "":
        errors.append(ValueError("Name is empty"))
    if not age.isdigit():
        errors.append(TypeError("Age must be a number"))
    if "@" not in email:
        errors.append(ValueError("Email needs an @ sign"))
    if visits < 0:
        errors.append(ValueError("Visits cannot be negative"))
    if errors:
        raise ExceptionGroup("Form has problems", errors)
    print("Form is fine")

try:
    check_form("", "ten", "ada.example.com", -1)
except* ValueError as group:
    print("Value problems:", len(group.exceptions))
    for err in group.exceptions:
        print(err)
except* TypeError as group:
    print("Type problems:", len(group.exceptions))
    for err in group.exceptions:
        print(err)

Key points

  • Exception group: one exception that bundles several exceptions together (Python 3.11+)
  • Use ExceptionGroup(name, errors) to build a group from a list of errors
  • Use group.exceptions to read the bundled errors as a tuple
  • Use except* to catch only the errors of one type from a group
  • Unmatched errors stay in a smaller group and are raised again afterwards

In this example, check_form checks all four fields, even after one fails. The empty name adds a ValueError to the list. The word ten is not digits, so the age check adds a TypeError. The email has no @ sign, so it adds another ValueError. The negative visits count adds one more ValueError. Because the list is not empty, the function raises one ExceptionGroup. The first except* block takes the three ValueError items and prints them. The second except* block takes the single TypeError and prints it.

Note

One try can hold several except* blocks, but plain except catches a group only as a whole.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Python Playground

Exercise

A group holds two ValueError items and one TypeError item. What does except* ValueError receive?