How to catch exceptions, respond cleanly, and keep your program running when something goes wrong.
Types of errors
Python has two broad categories:
- Syntax errors: the code breaks Python’s parsing rules and will not run at all.
- Exceptions: the syntax is fine, but something fails at runtime – wrong type, missing file, division by zero, and so on.
The try-except block
Wrap risky code in try. If an exception is raised, the matching except block runs instead of crashing:
try:
# Code that might cause an exception
result = 10 / 0
except ZeroDivisionError:
# Code that runs if the exception occurs
print("You can't divide by zero!")
Handling multiple exceptions
Add separate except blocks for each error type. Catch specific exceptions rather than a bare except – it keeps your intent clear:
try:
number = int(input("Enter a number: "))
result = 10 / number
except ZeroDivisionError:
print("Division by zero is not allowed.")
except ValueError:
print("Invalid input. Please enter a number.")
The else clause
else runs only when no exception occurred in the try block:
try:
number = int(input("Enter a number: "))
except ValueError:
print("That's not a number!")
else:
print(f"You entered {number}")
The finally block
finally always runs, whether or not an exception occurred. Use it for cleanup:
try:
file = open('example.txt')
data = file.read()
except FileNotFoundError:
print("The file was not found.")
finally:
file.close()
print("File closed.")
Raising exceptions
Sometimes you need to signal a problem yourself with raise:
def calculate_age(birth_year):
current_year = 2021
age = current_year - birth_year
if age < 0:
raise ValueError("Birth year cannot be in the future")
return age
try:
my_age = calculate_age(2025)
except ValueError as err:
print(err)
Custom exceptions
Subclass Exception to define errors specific to your application:
class NegativeAgeError(Exception):
"""Exception raised for errors in the input birth year."""
def __init__(self, birth_year, message="Birth year cannot be in the future"):
self.birth_year = birth_year
self.message = message
super().__init__(self.message)
try:
age = calculate_age(2025)
except NegativeAgeError as e:
print(f"Error: {e}")
Best practices
- Be specific: catch named exceptions, not a bare
except. - Keep try blocks small: easier to see which line failed.
- Use finally for cleanup: close files, release resources.
- Custom exceptions for clarity: name errors that match your domain.
The goal is not error-free code – it is code that fails gracefully and tells the user (or you) what went wrong.

