
How to Catch Multiple Exceptions in Python: Try-Except Best Practices
Writing robust, production-grade applications requires handling errors gracefully. If a database query fails, a network connection drops, or a user submits invalid calculations, your application must capture the exception rather than crashing.
In Python, you manage these runtime errors using the try-except structure.
Frequently, a single block of code can raise different types of errors (e.g., dividing by zero or attempting to parse an invalid string).
In this guide, we will analyze three methods to catch multiple exceptions, inspect Python's exception class hierarchy, and establish safe error-handling guidelines.
Method 1: Using Multiple except Blocks (Distinct Handling)
If you need to execute different operations depending on the specific error that occurred, use multiple consecutive except statements.
def process_data(data_string, divisor):
try:
# Might raise ValueError
number = int(data_string)
# Might raise ZeroDivisionError
result = 100 / divisor
return result
except ValueError as e:
print(f"Parsing error: {e}. Please submit a valid integer.")
return None
except ZeroDivisionError as e:
print(f"Math error: {e}. Divisor cannot be zero.")
return 0Python evaluates the except blocks from top to bottom. Once a match is found, that block executes, and the rest are ignored.
Method 2: Grouping Exceptions in a Parenthesized Tuple (Shared Handling)
If you want to apply the exact same recovery logic to different exception types, you can group them inside a parenthesized tuple:
def read_configuration(file_path):
try:
with open(file_path, "r") as f:
return f.read()
except (FileNotFoundError, PermissionError) as e:
print(f"System access warning: {e}")
return "Default Config"- Important Syntax Rule: You must enclose the exceptions in parentheses (e.g.,
(ErrorA, ErrorB)). If you omit the parentheses (e.g.,except FileNotFoundError, PermissionError), Python will fail to compile (or in legacy Python 2, it misinterpretsPermissionErroras the variable name storing theFileNotFoundErrorinstance).
Method 3: Leveraging the Exception Inheritance Tree
Python's built-in exceptions are organized in an inheritance hierarchy. If you catch a parent exception class, you automatically capture all of its subclasses.
For example, ZeroDivisionError and OverflowError inherit from the ArithmeticError parent class:
try:
result = 1 / 0
except ArithmeticError as e:
# Catches ZeroDivisionError, FloatingPointError, and OverflowError
print(f"Arithmetic calculation failure: {e}")Refer to this common built-in hierarchy:
BaseExceptionSystemExitKeyboardInterruptException(All standard runtime errors inherit from here)ArithmeticErrorZeroDivisionError
LookupErrorIndexErrorKeyError
The Golden Rule: Catch Specific Exceptions First
Always declare specific child exceptions before broad parent exceptions. If you catch a broad exception first, it intercepts all errors, leaving the specific catch blocks unreachable:
# AVOID: Parent exception catches everything, leaving ValueError unreachable
try:
num = int("abc")
except Exception as e:
print("Caught generic exception")
except ValueError as e:
print("This block will never run!")Additionally, avoid bare except clauses (except:) or catching the root Exception globally without logging. This masks programming typos, syntax errors, and system signals (like Ctrl+C / KeyboardInterrupt), making code difficult to debug.
Conclusion
Catching multiple exceptions in Python keeps applications stable under varying runtime conditions. Use separate except statements to apply custom recovery steps for distinct error types, group exceptions inside parenthesized tuples to share generic recovery logic, and prioritize catching specific child classes first to prevent generic exception traps from masking bugs.