Basic

Error Handling

Handle failures gracefully with try/except and raise custom errors.

What you will see

File gone

About 2 min of reading

Quick check

Before this new lesson, one small question about the lesson you just finished. Recall it, do not look it up.

Try It

Errors are handled, not just feared. I catch a divide-by-zero and keep going, use a specific handler with a finally cleanup, raise my own guard, follow the ask-forgiveness pattern, and see what happens when a file is gone.

1

A matching except runs only when its error happens. I divide by zero, catch the specific ZeroDivisionError, print a message, and confirm the line after still runs.

try: result = 10 / 0 except ZeroDivisionError as e: print("Cannot divide by zero") print("after")

Before you run it, say out loud what it will print. Then open the answer.

What it prints
Cannot divide by zero after
Catch the most specific error you can. A broad except can hide real bugs later in the same block.
2

I can list several handlers and always run a cleanup. I try to turn text into a number, fail on the ValueError, then print 'Done' from finally so it runs no matter what.

try: value = "abc" n = int(value) except ValueError: print("Not a number") finally: print("Done")

Before you run it, say out loud what it will print. Then open the answer.

What it prints
Not a number Done
finally runs whether or not an error occurred, which is why it is the place to close files or connections.
3

I can throw my own error when a rule breaks. A withdraw guard raises when the amount is too high, and the caller catches it to print the message.

def withdraw(amount): if amount > 100: raise ValueError("Limit exceeded") return amount try: withdraw(150) except ValueError as e: print(e)

Before you run it, say out loud what it will print. Then open the answer.

What it prints
Limit exceeded
raise turns a detected problem into something the caller must handle. Pair it with a specific error type so handlers can tell it apart.
4

Ask forgiveness, not permission: try the action, then handle the missing case. I read a present key and a missing one the same way and print what each returns.

data = {"key": "value"} try: value = data["key"] except KeyError: value = "default" print(value) try: value2 = data["missing"] except KeyError: value2 = "default" print(value2)

Before you run it, say out loud what it will print. Then open the answer.

What it prints
value default
This is cleaner than checking if "key" in data first, but only when the missing case is actually unusual.
5

When a file is gone the same kind of error fires. I attempt to open a file that does not exist and catch the specific FileNotFoundError to report it instead of crashing.

import os os.chdir("/tmp") try: f = open("missing.txt") f.close() except FileNotFoundError: print("File gone")

Before you run it, say out loud what it will print. Then open the answer.

What it prints
File gone
Use a with statement in real code so the file closes even if the read raises; I opened it manually here just to see the error.

Put it in order

Before you write anything, put these lines back in the right order. Click a line to move it up one step. This support step is easier than writing code from scratch, and it is here to make the last example stick.

Data Flow

Code runs in a try block; on error control jumps to an except handler, then always to finally.
1 try block starts running
↓
2 Exception raised -> normal flow breaks
↓
3 Matching except catches it -> handler runs
↓
4 finally runs -> block exits normally
Line by line

Read the first example one line at a time. A line that prints shows what appears on screen; the rest set things up quietly.

try: sets nothing on screen
result = 10 / 0 sets nothing on screen
except ZeroDivisionError as e: sets nothing on screen
print("Cannot divide by zero") Cannot divide by zero
print("after") after
Why this works
The exception hierarchy

All exceptions inherit from Exception. Catch the most specific type you can:

try:
    open("missing.txt")
except FileNotFoundError:
    print("File gone")
except OSError:
    print("Other I/O error")

Catching broad Exception hides bugs; catching BaseException can swallow keyboard interrupts.

When you have a choice

When to use exceptions

Use exceptions for exceptional conditions (bad input, missing files), not for normal control flow. A common Python pattern is EAFP, "easier to ask forgiveness than permission":

try:
    value = data["key"]
except KeyError:
    value = default

This is cleaner than checking if "key" in data first.

Examples

Catching exceptions

try/except

try:
    result = 10 / 0
except ZeroDivisionError as e:
    print("Cannot divide by zero")

The except block runs only if the matching error is raised. Execution then continues after the block.

Multiple handlers and finally

Specific handlers

try:
    value = int(input("Number: "))
except ValueError:
    print("Not a number")
except Exception as e:
    print("Other error", e)
finally:
    print("Done")   # always runs

finally runs whether or not an error occurred, ideal for cleanup like closing files.

Raising your own errors

Validate with raise

def withdraw(amount):
    if amount > 100:
        raise ValueError("Limit exceeded")
    return amount

try:
    withdraw(150)
except ValueError as e:
    print(e)

raise turns a detected problem into an exception the caller can catch.

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← Lists and Dictionaries
Previous Lesson
Lists and Dictionaries
Next Lesson
Data Structures Deep Dive
Data Structures Deep Dive →