Use ExceptionGroup except star for independent validation failures
To use ExceptionGroup and except* for independent validation failures, run each validator, collect its exception, then raise one group after all validators have had a chance to run. The contrast starts with sequential validation: the first ValueError stops the loop, so the later TypeError is never reported. The collection branch instead records both failures and the successful validator contributes no error.
except* ValueError and except* TypeError each receive a matching subgroup. The example extracts messages from their direct members, asserts that each handler saw the intended message, and prints both results. Because every member is handled, no residual exception group propagates. This establishes the behavior of this controlled two-error group; it does not guarantee how a larger application should prioritize, display, or recover from all validation errors.
ExceptionGroup and except* require Python 3.11 or later. They are especially appropriate when validations are intentionally independent and the caller benefits from seeing all collected failures at once. If a later validation depends on an earlier valid result, keep the sequential dependency instead of forcing it into a group. The Python tutorial documents grouping exception instances and explains that each except* clause extracts matching members. Python error and exception tutorial
AI assistance disclosure: This synthetic example and explanation were prepared with AI assistance and should be adapted to the application’s validation model.
def positive_count():
raise ValueError("count must be positive")
def boolean_enabled():
raise TypeError("enabled must be bool")
def supported_mode():
return "safe"
validators = [positive_count, boolean_enabled, supported_mode]
try:
for validator in validators:
validator()
except Exception as error:
first_failure = f"{type(error).__name__}: {error}"
failures = []
for validator in validators:
try:
validator()
except Exception as error:
failures.append(error)
handled = []
try:
raise ExceptionGroup("invalid settings", failures)
except* ValueError as group:
messages = [str(error) for error in group.exceptions]
assert messages == ["count must be positive"]
handled.append(f"ValueError messages: {', '.join(messages)}")
except* TypeError as group:
messages = [str(error) for error in group.exceptions]
assert messages == ["enabled must be bool"]
handled.append(f"TypeError messages: {', '.join(messages)}")
assert handled == [
"ValueError messages: count must be positive",
"TypeError messages: enabled must be bool",
]
print(f"sequential first: {first_failure}")
for line in handled:
print(line)
sequential first: ValueError: count must be positive
ValueError messages: count must be positive
TypeError messages: enabled must be bool