Skip to main content

How to Catch Multiple Exceptions in One Line in Python

When handling errors in Python, you often encounter situations where different exceptions should be handled in the same way like displaying the same error message, logging the same warning, or executing the same fallback logic. Instead of writing separate except blocks for each exception type, Python lets you catch multiple exceptions in a single line using a tuple.

This approach keeps your code cleaner, shorter, and follows the DRY (Don't Repeat Yourself) principle.

Basic Syntax

Catch multiple exceptions by grouping them in a tuple within a single except clause:

try:
# Code that might raise different exceptions
pass
except (ExceptionType1, ExceptionType2, ExceptionType3) as e:
# Handle all of them the same way
print(f"An error occurred: {e}")

The Problem: Repetitive Exception Handling

Without grouping, handling multiple exceptions with the same logic requires duplicate code:

# Repetitive: violates DRY principle
try:
result = int("hello")
except ValueError as e:
print(f"Error: {e}")
except TypeError as e:
print(f"Error: {e}")
except ArithmeticError as e:
print(f"Error: {e}")

Each except block does the exact same thing. This is repetitive and harder to maintain.

The Solution: Catch Multiple Exceptions in One Line

Group the exceptions into a tuple to handle them all with a single block:

try:
result = int("hello")
except (ValueError, TypeError, ArithmeticError) as e:
print(f"Error: {e}")

Output:

Error: invalid literal for int() with base 10: 'hello'

The same handler runs regardless of which exception in the tuple is raised.

Practical Examples

Example 1: Handling User Input Errors

def safe_divide(a, b):
try:
result = a / b
return result
except (ZeroDivisionError, TypeError) as e:
print(f"Cannot divide: {e}")
return None

# Division by zero
print(safe_divide(10, 0))

# Wrong type
print(safe_divide(10, "hello"))

Output:

Cannot divide: division by zero
None
Cannot divide: unsupported operand type(s) for /: 'int' and 'str'
None

Both ZeroDivisionError and TypeError are handled by the same block.

Example 2: File and Data Processing

import json

def load_config(filepath):
try:
with open(filepath, 'r') as f:
config = json.load(f)
return config
except (FileNotFoundError, PermissionError, json.JSONDecodeError) as e:
print(f"Failed to load config: {e}")
return {}

# Test with a non-existent file
config = load_config("missing_config.json")
print(f"Config: {config}")

Output:

Failed to load config: [Errno 2] No such file or directory: 'missing_config.json'
Config: {}

Three different exceptions ae file not found, permission denied, and invalid JSON and they are all handled the same way.

Example 3: Data Conversion

def convert_to_number(value):
try:
return float(value)
except (ValueError, TypeError) as e:
print(f"Cannot convert '{value}': {e}")
return 0.0

# Various invalid inputs
print(convert_to_number("3.14")) # Works
print(convert_to_number("hello")) # ValueError
print(convert_to_number(None)) # TypeError
print(convert_to_number([1, 2])) # TypeError

Output:

3.14
Cannot convert 'hello': could not convert string to float: 'hello'
0.0
Cannot convert 'None': float() argument must be a string or a real number, not 'NoneType'
0.0
Cannot convert '[1, 2]': float() argument must be a string or a real number, not 'list'
0.0

Combining Grouped and Separate Exception Handlers

You can mix grouped and individual except blocks when some exceptions need different handling:

def process_data(data):
try:
result = 100 / int(data)
return result
except (ValueError, TypeError) as e:
# Handle input conversion errors the same way
print(f"Invalid input: {e}")
return None
except ZeroDivisionError:
# Handle division by zero differently
print("Error: Cannot divide by zero. Using default value.")
return float('inf')

print(process_data("hello")) # ValueError
print(process_data(None)) # TypeError
print(process_data("0")) # ZeroDivisionError
print(process_data("5")) # Success

Output:

Invalid input: invalid literal for int() with base 10: 'hello'
None
Invalid input: int() argument must be a string, a bytes-like object or a real number, not 'NoneType'
None
Error: Cannot divide by zero. Using default value.
inf
20.0

Identifying Which Exception Was Raised

When catching multiple exceptions in one block, you can check which specific exception occurred using type() or isinstance():

def parse_value(value):
try:
return int(value) / (int(value) - 5)
except (ValueError, TypeError, ZeroDivisionError) as e:
error_type = type(e).__name__
print(f"[{error_type}] {e}")

if isinstance(e, ZeroDivisionError):
print(" → Hint: The value results in division by zero.")
elif isinstance(e, ValueError):
print(" → Hint: Provide a valid integer string.")

parse_value("hello")
parse_value(None)
parse_value("5")

Output:

[ValueError] invalid literal for int() with base 10: 'hello'
→ Hint: Provide a valid integer string.
[TypeError] int() argument must be a string, a bytes-like object or a real number, not 'NoneType'
[ZeroDivisionError] division by zero
→ Hint: The value results in division by zero.

Using Exception Hierarchies

Python exceptions follow an inheritance hierarchy. Catching a parent exception also catches all its children:

# ArithmeticError is the parent of:
# - ZeroDivisionError
# - OverflowError
# - FloatingPointError

try:
result = 1 / 0
except ArithmeticError as e:
print(f"Math error: {e}")
print(f"Specific type: {type(e).__name__}")

Output:

Math error: division by zero
Specific type: ZeroDivisionError
Use parent exceptions for broad catching

Instead of listing many related exceptions individually:

except (ZeroDivisionError, OverflowError, FloatingPointError):

You can catch the parent:

except ArithmeticError:

Common parent exceptions:

  • OSError: covers FileNotFoundError, PermissionError, ConnectionError, etc.
  • ArithmeticError: covers ZeroDivisionError, OverflowError, FloatingPointError
  • LookupError: covers IndexError, KeyError

Common Patterns

Dictionary/List Access Errors

def get_value(data, key):
try:
return data[key]
except (KeyError, IndexError, TypeError) as e:
print(f"Access error: {e}")
return None

print(get_value({"a": 1}, "b")) # KeyError
print(get_value([1, 2, 3], 10)) # IndexError
print(get_value(None, "key")) # TypeError

Output:

Access error: 'b'
None
Access error: list index out of range
None
Access error: 'NoneType' object is not subscriptable
None

Network/IO Operations

import urllib.request

def fetch_url(url):
try:
response = urllib.request.urlopen(url, timeout=5)
return response.read()
except (urllib.error.URLError, urllib.error.HTTPError,
TimeoutError, ConnectionError) as e:
print(f"Network error: {e}")
return None

Summary

ApproachSyntaxUse Case
Single exceptionexcept ValueError:One specific exception
Multiple (tuple)except (ValueError, TypeError):Same handling for multiple exceptions
Parent exceptionexcept ArithmeticError:Catch all child exceptions
MixedGrouped + separate blocksDifferent handling for different groups
With aliasexcept (ValueError, TypeError) as e:Access the exception message

Conclusion

Catching multiple exceptions in one line using a tuple is a clean, Pythonic pattern that reduces code duplication and improves readability.

  • Group exceptions together when they need the same handling logic, and use separate except blocks when different exceptions need different responses.
  • Leverage Python's exception hierarchy to catch related exceptions with a single parent class.
  • The as e clause gives you access to the specific error message regardless of which exception was raised, making your error handling both concise and informative.