Python ExceptionGroup and except*

Python can report several related failures at once with exception groups. This is useful when one operation starts several independent tasks and more than one task fails. Instead of hiding every failure except the first, you can raise one structured error and handle selected exception types with except*.

Exception groups and except* became available in Python 3.11. They preserve the original exceptions, their tracebacks, and any nested grouping, so you can see which operations failed and respond accurately.

Use an exception group when failures belong to one operation but can occur independently. For a single failure, raise a normal exception.

What Is an ExceptionGroup?

ExceptionGroup stores a message and a non-empty sequence of exceptions. Every item must inherit from Exception. If you need to include exceptions such as KeyboardInterrupt or SystemExit, use BaseExceptionGroup instead.

Syntax:

# Group two independent validation failures
errors = ExceptionGroup(
    "Profile validation failed",
    [
        ValueError("Age must be 18 or older"),
        TypeError("Phone number must be text"),
    ],
)
raise errors

The traceback displays the group as a tree. Each branch keeps its own exception type, message, and traceback, which makes related failures easier to inspect.

Handle Matching Errors with except*

An except* clause receives a subgroup containing only the matching leaf exceptions. Python passes the remaining exceptions to later clauses. Any unmatched part is raised again after the handlers finish.

Example:

def import_users():
    # Collect errors produced by independent records
    raise ExceptionGroup(
        "User import failed",
        [
            ValueError("Asha has an invalid age"),
            TypeError("Oliver has an invalid phone"),
            ValueError("Mia has an invalid postal code"),
        ],
    )

try:
    import_users()
except* ValueError as value_group:
    # Handle every ValueError branch together
    for error in value_group.exceptions:
        print("Value issue:", error)
except* TypeError as type_group:
    # Handle the TypeError branch separately
    for error in type_group.exceptions:
        print("Type issue:", error)

Both handlers run because each one receives a different matching subgroup. This differs from ordinary except, where the first matching handler completes the choice.

Work with Nested Groups

Groups can contain other groups. Python keeps that shape when except* splits a group, which helps you retain context such as a service name or batch number.

Example:

# Keep failures grouped by the operation that produced them
batch_error = ExceptionGroup(
    "Nightly update failed",
    [
        ExceptionGroup("London office", [ConnectionError("Server did not respond")]),
        ExceptionGroup("Delhi office", [ValueError("Employee ID is missing")]),
    ],
)

try:
    raise batch_error
except* ConnectionError as network_errors:
    # Retry or log only network-related branches
    print("Network failures:", network_errors)

Inspect or Split a Group

Use subgroup(condition) to obtain matching branches. Use split(condition) when you need both the matching and remaining parts. These methods accept an exception type or callable condition and preserve traceback information.

Method Result Typical Use
subgroup() Matching subgroup or None Inspect selected errors
split() Matching and remaining groups Route failures to different logic
derive() New group with replacement exceptions Support a custom group subclass

Example:

group = ExceptionGroup(
    "Checks failed",
    [ValueError("Invalid total"), OSError("File unavailable")],
)

# Separate validation errors from all other failures
matched, remaining = group.split(ValueError)
print(matched)
print(remaining)

Rules for except*

  • Do not mix except and except* clauses in the same try statement.
  • Do not use return, break, or continue inside an except* block.
  • Raise a new exception inside a handler only when that failure should join the final result.
  • Catch narrow exception types so unrelated failures remain visible.

When to Use Exception Groups

Exception groups suit concurrent tasks, batch validation, cleanup steps, and libraries that must report several independent failures. Avoid changing an existing function to raise groups without reviewing callers, because code that expects one ordinary exception may need new handling.

Conclusion

Python exception groups let you preserve and report several related failures without losing their individual details. Create an ExceptionGroup, use except* to handle matching branches, and leave unmatched failures visible. This approach gives concurrent and batch operations clearer, more complete error handling.



Found This Page Useful? Share It!
Get the Latest Tutorials and Updates
Join us on Telegram