Exception Handling
Learn how to catch and handle runtime errors gracefully in Python using try, except, else, finally, and raise.
Introduction
No matter how carefully you write code, things can go wrong at runtime: a user might type text instead of a number, a file might be missing, or a network request might fail. These runtime problems are called exceptions, and if left unhandled, they crash your program immediately.
Python gives you a clean way to anticipate and handle these problems using try, except, else, and finally. This lesson teaches you how to catch errors gracefully, respond to specific problems differently, and raise your own errors when something is wrong.
- How try and except catch runtime errors before they crash your program.
- How to catch specific exception types like ValueError and ZeroDivisionError.
- What the else and finally clauses do and when to use them.
- How to raise your own exceptions using raise.
- Why catching specific exceptions is better than a bare except.
Why Exception Handling Matters
Without exception handling, a single unexpected error stops your entire program dead, printing a scary-looking traceback and losing any unsaved work. Exception handling lets your program anticipate likely problems and respond sensibly — showing a friendly message, retrying, or skipping the problem — instead of crashing.
Prevents Crashes
Catch errors before they stop your entire program.
Friendlier Messages
Show a helpful message instead of a raw traceback.
Recovery
Retry an operation or fall back to a default value.
Cleanup
Guarantee that resources like files or connections are closed properly.
try and except
Code that might fail goes inside a try block. If an error occurs, Python immediately jumps to the matching except block instead of crashing the program.
try:
number = int(input("Enter a number: "))
print("You entered:", number)
except:
print("That was not a valid number!")That was not a valid number!The bare except: above catches any error at all, which is convenient but hides useful information about exactly what went wrong. The next section shows a better approach.
Catching Specific Exception Types
Python has many built-in exception types, each describing a specific kind of problem. Catching the specific type lets you respond appropriately to each situation, and lets unexpected errors still surface clearly instead of being silently hidden.
| Exception | Common Cause |
|---|---|
| ValueError | Converting invalid data, like int('abc') |
| ZeroDivisionError | Dividing a number by zero |
| FileNotFoundError | Opening a file that does not exist |
| KeyError | Accessing a dictionary key that does not exist |
| TypeError | Using an operation on the wrong data type |
try:
number = int(input("Enter a number: "))
result = 100 / number
print("Result:", result)
except ValueError:
print("That is not a valid whole number.")
except ZeroDivisionError:
print("You cannot divide by zero.")You cannot divide by zero.Python checks each except clause in order and runs the first one that matches the type of exception that was raised.
data = {"name": "Alex", "age": 16}
try:
with open("missing.txt", "r") as file:
print(file.read())
print(data["email"])
except FileNotFoundError:
print("The file could not be found.")
except KeyError:
print("That key does not exist in the dictionary.")The file could not be found.Notice that as soon as the FileNotFoundError happens, Python jumps straight to its except block — the print(data["email"]) line never even runs, so its KeyError never gets a chance to occur here.
The else Clause
An optional else block runs only if the try block completed with no exceptions at all. It is useful for code that should run only on success, keeping it separate from the risky code in try.
try:
number = int("42")
except ValueError:
print("Conversion failed.")
else:
print("Conversion succeeded! Number is:", number)Conversion succeeded! Number is: 42The finally Clause
A finally block always runs, whether an exception occurred or not, and even if the exception was not caught by any except clause. It is the ideal place for cleanup code, like closing a file or a network connection.
try:
number = int("not a number")
except ValueError:
print("Could not convert to a number.")
finally:
print("This always runs, no matter what.")Could not convert to a number.
This always runs, no matter what.- try: code that might raise an exception.
- except: runs only if a matching exception occurred.
- else: runs only if no exception occurred.
- finally: always runs, no matter what happened.
Raising Exceptions with raise
Sometimes you want to signal an error yourself, rather than waiting for Python to raise one naturally. The raise keyword lets you trigger an exception on purpose, which is especially useful inside functions that validate their input.
def withdraw(balance, amount):
if amount > balance:
raise ValueError("Insufficient funds for this withdrawal.")
return balance - amount
try:
new_balance = withdraw(100, 250)
except ValueError as error:
print("Error:", error)Error: Insufficient funds for this withdrawal.Using as error captures the exception object so you can inspect or print its message, rather than just knowing that some error occurred.
Custom Exceptions (A Quick Preview)
Python also lets you define your own exception types by creating a class that inherits from Exception. This is a preview only — classes and inheritance are covered in depth starting with the next few lessons on object-oriented programming.
class InsufficientFundsError(Exception):
pass
def withdraw(balance, amount):
if amount > balance:
raise InsufficientFundsError("Not enough money in the account.")
return balance - amount
try:
withdraw(100, 500)
except InsufficientFundsError as error:
print("Custom error caught:", error)Custom error caught: Not enough money in the account.Common Mistakes
- Using a bare except: that silently swallows every error, including bugs you did not expect.
- Catching Exception broadly when a specific exception type would give clearer, safer handling.
- Putting too much code inside try, making it unclear exactly which line might fail.
- Forgetting that finally runs even after a return statement inside try or except.
- Using exceptions for normal control flow instead of for truly exceptional situations.
Best Practices
- Catch specific exception types like ValueError or KeyError instead of a bare except.
- Keep the try block as small as possible, containing only the risky code.
- Use finally for cleanup that must always happen, like closing files or connections.
- Give raised exceptions clear, descriptive messages that explain what went wrong.
- Avoid hiding real bugs by catching exceptions you do not know how to handle.
Frequently Asked Questions
What is the difference between except: and except Exception:?
A bare except: catches literally everything, including things like keyboard interrupts. except Exception: is slightly more controlled but still broad. Catching specific types like ValueError is the safest, clearest approach.
Does finally run if I use return inside try?
Yes. The finally block always runs before the function actually returns, even if try or except contains a return statement.
Can I catch multiple exception types in one except clause?
Yes, by grouping them in a tuple, like except (ValueError, TypeError): to handle both the same way.
When should I use raise instead of just returning an error value?
Use raise when a problem is truly exceptional and the calling code should be forced to notice it, rather than silently continuing with a default or error value.
Do I need to learn classes before writing custom exceptions?
Yes, a solid understanding of classes and inheritance helps you write custom exceptions properly. You will get there very soon, starting with the OOP lessons right after this one.
Key Takeaways
- try and except let you catch runtime errors before they crash your program.
- Catching specific exception types like ValueError, ZeroDivisionError, FileNotFoundError, and KeyError is safer than a bare except.
- else runs only when no exception occurred; finally always runs.
- raise lets you trigger your own exceptions, often to validate input inside functions.
- Custom exception classes are possible by inheriting from Exception, a topic you will deepen once classes are covered.
Summary
Exception handling lets your Python programs anticipate and gracefully recover from runtime errors instead of crashing. You learned how try and except catch problems, how else and finally control what runs and when, and how raise lets you signal your own errors.
- You can catch and handle runtime errors using try and except.
- You know how to target specific exception types.
- You understand else, finally, and raise.
- You are ready to begin object-oriented programming.