Python Error Handling

Catch and handle exceptions with try, except, else and finally, and raise your own errors with clear messages.

Lesson 30 of 107Intermediate18 min read

Even perfect code meets imperfect situations. A user types "ten" when you asked for a number, a file has been deleted, a website doesn't answer, or a list turns out to be empty. When Python hits a problem it can't handle, it raises an exception, stops the program and prints an error message.

Crashing isn't always the right reaction. Often you'd rather show a friendly message, ask again, use a default value or skip one bad record and carry on with the rest. Python lets you do exactly that with try and except: you try the risky code, and if a particular error happens, you handle it.

In this lesson you'll learn to read error messages, catch specific exceptions, run clean-up code with finally, and raise your own exceptions so that problems are reported clearly. These skills turn scripts that only work on good days into programs people can rely on.

Reading a traceback

When an exception isn't handled, Python prints a traceback:

Text
Traceback (most recent call last):
  File "shop.py", line 4, in <module>
    total = price * int(quantity)
ValueError: invalid literal for int() with base 10: 'two'

Read it from the bottom up. The last line names the exception type, ValueError, and explains what went wrong. The lines above show where it happened: the file, the line number and the code on that line. Most bugs can be found just by reading these few lines carefully.

Common exception types

ExceptionTypical cause
ValueErrorRight type, wrong value: int("two")
TypeErrorWrong type: "5" + 5
NameErrorA variable name that doesn't exist
IndexErrorA list position that doesn't exist
KeyErrorA dictionary key that doesn't exist
ZeroDivisionErrorDividing by zero
FileNotFoundErrorOpening a file that isn't there

Knowing the name of the error you expect is important, because you'll catch exceptions by type.

try and except

Put the code that might fail inside try. If an exception of the named type happens, Python jumps to the matching except block instead of crashing:

Python
try:
    age = int(input("Age: "))
except ValueError:
    print("Please type a whole number.")
  • If the code in try runs without problems, the except block is skipped.
  • You can have several except blocks for different error types.
  • except ValueError as error: gives you the exception object, so you can print its message.
  • Only wrap the lines that can actually fail. Big try blocks hide bugs.

else, finally and raise

Two optional blocks complete the picture:

  • else: runs only if no exception happened. It's a good place for code that depends on the risky step succeeding.
  • finally: runs always, whether there was an error or not. Use it for clean-up such as closing a connection.

You can also raise an exception yourself when your function receives something it can't work with:

Python
def set_age(age):
    if age < 0:
        raise ValueError("Age can't be negative")

Raising early with a clear message is far better than letting a bad value travel through your program and cause a confusing error somewhere else.

Custom exceptions and re-raising

For your own programs, you can define exception types that describe your problem domain. A custom exception is a class that inherits from Exception, and it often needs no body at all:

Python
class OutOfStockError(Exception):
    """Raised when an order asks for more items than are available."""

def reserve(stock, quantity):
    if quantity > stock:
        raise OutOfStockError(f"Only {stock} left")
    return stock - quantity

Callers can now catch OutOfStockError specifically, without accidentally catching unrelated ValueErrors. You'll learn more about classes and inheritance later in the course.

Sometimes you want to note an error and still let it continue upwards. A bare raise inside an except block re-raises the current exception unchanged. To translate a low-level error into a clearer one, use raise ... from ..., which keeps the original exception attached to the traceback for debugging:

Python
try:
    port = int(setting)
except ValueError as error:
    raise ValueError(f"Port must be a number, got {setting!r}") from error

Python Error Handling Syntax

try / except / else / finally

try:
    risky_code()
except ValueError as error:
    print("Bad value:", error)
except (KeyError, IndexError):
    print("Missing item")
else:
    print("It worked")
finally:
    print("Always runs")

raise ValueError("Explain what went wrong")

Python Error Handling Summary

  • Read tracebacks from the bottom: the type and message, then the line number.
  • try wraps risky code; except SomeError handles that error type.
  • Catch specific exceptions, and keep the try block small.
  • else runs when nothing failed; finally always runs.
  • raise ValueError("message") reports a problem clearly.

Python Error Handling Examples

Each example shows the code, the output it produces and what each part does.

Example 1

Asking until the input is valid

Here the user typed ten, then -3, then 12.

Python
while True:
    try:
        quantity = int(input("How many tickets? "))
        if quantity < 1:
            raise ValueError("must be at least 1")
    except ValueError as error:
        print("Try again:", error)
    else:
        break

print(f"Booking {quantity} tickets")
Output
How many tickets? ten
Try again: invalid literal for int() with base 10: 'ten'
How many tickets? -3
Try again: must be at least 1
How many tickets? 12
Booking 12 tickets
Line-by-line explanation
  1. Line 1Keep asking until the input is valid.
  2. Line 3int() raises ValueError for text that isn't a whole number.
  3. Lines 4 to 5Our own rule raises the same type of error, with our own message.
  4. Lines 6 to 7Both kinds of problem are handled in one place.
  5. Lines 8 to 9else only runs when nothing went wrong, so the loop ends.
Example 2

Handling different errors differently

Python
prices = {"tea": 2.5, "coffee": 3.2}

def price_per_person(item, people):
    try:
        return prices[item] / people
    except KeyError:
        return f"We don't sell {item}"
    except ZeroDivisionError:
        return "Need at least one person"

print(price_per_person("tea", 2))
print(price_per_person("cake", 2))
print(price_per_person("coffee", 0))
Output
1.25
We don't sell cake
Need at least one person
Line-by-line explanation
  1. Line 5Two different things can go wrong on this line.
  2. Lines 6 to 9Each exception type gets its own helpful message.
Example 3

finally for clean-up

Python
def load_report(name):
    print("Opening connection")
    try:
        if name == "":
            raise ValueError("Report name is empty")
        return f"Report: {name}"
    finally:
        print("Closing connection")

print(load_report("sales"))
try:
    load_report("")
except ValueError as e:
    print("Error:", e)
Output
Opening connection
Closing connection
Report: sales
Opening connection
Closing connection
Error: Report name is empty
Line-by-line explanation
  1. Lines 7 to 8finally runs before the function returns, and also when an error is raised.
  2. Lines 11 to 14The caller decides how to handle the error raised inside the function.

Common Python Error Handling Mistakes

Catching every exception

Don't
try:
    total = sum(prcies)   # typo
except:
    total = 0
Do
try:
    total = sum(prices)
except TypeError:
    total = 0

The bare except hides the NameError caused by the typo, so the bug goes unnoticed.

Wrapping too much code

Don't
try:
    data = load_data()
    report = build_report(data)
    send_email(report)
except ValueError:
    print("Something went wrong")
Do
data = load_data()
try:
    report = build_report(data)
except ValueError as error:
    print("Couldn't build the report:", error)
else:
    send_email(report)

Using exceptions instead of a simple check

Don't
try:
    first = items[0]
except IndexError:
    first = None
Do
first = items[0] if items else None

When a plain if is clearer, use it. Save try/except for errors you can't easily check in advance.

Python Error Handling in Real Projects

Error handling in practice

Web servers wrap each request in error handling so that one bad request returns an error page instead of taking the whole site down. Data pipelines catch errors on individual rows, log them and keep processing the rest. Libraries raise clear, specific exceptions, such as requests.Timeout, so that your code can decide whether to retry or give up.

Python Error Handling FAQ

What is the difference between an error and an exception in Python?

In everyday speech they mean the same thing. Technically, an exception is the object Python raises when something goes wrong at run time, and most built-in ones have names ending in Error. Syntax errors are found before the program runs, so they can't be caught by a try block in the same file.

What is the difference between except: and except Exception:?

A bare except: catches everything, including KeyboardInterrupt and SystemExit, so even Ctrl+C can't stop your program. except Exception: lets those through but still catches almost every error. Both are too broad for normal code; catch the specific exceptions you expect.

Does finally still run if the try block returns?

Yes. finally runs whether the try block finishes normally, returns, or raises an exception. That's what makes it reliable for clean-up. Avoid putting a return inside finally itself, because it silently replaces any exception or earlier return value.

How do I print the full traceback without stopping the program?

Inside an except block, call traceback.print_exc() from the traceback module, or logging.exception("message"), which logs the message together with the full traceback. Both let you record the details and then carry on.

Should I check first with if, or use try and except?

Both styles are valid. Use if when the check is simple and cheap, such as an empty list. Use try/except when checking first is awkward or unreliable, for example converting text to a number or opening a file that another program might delete between your check and your read.

Python Error Handling Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Fill in the blankseasy

Complete the code so a bad number is handled instead of crashing.

:
    n = int("abc")
 ValueError:
    print("Not a number")
Predict the outputeasy

What does this print?

try:
    print(10 / 0)
except ZeroDivisionError:
    print("Oops")
Predict the outputmedium

What does this print?

try:
    x = int("7")
except ValueError:
    print("A")
else:
    print("B")
finally:
    print("C")
Multiple choicemedium

Which exception does {"a": 1}["b"] raise?

Multiple choicehard

Why is except: with no exception type usually a bad idea?

Python Error Handling Quiz

5 questions to check what you learned.

  1. Where should you start reading a traceback?

    • At the top
    • In the middle
    • It doesn't matter
    • At the bottom, which names the error type and message
  2. Which exception does int("hello") raise?

    • TypeError
    • NameError
    • ValueError
    • KeyError
  3. When does a finally block run?

    • Only when there's an error
    • Always
    • Only when there's no error
    • Never inside functions
  4. What does raise ValueError("Too big") do?

    • Stops normal execution with a ValueError carrying that message
    • Prints "Too big" and continues
    • Fixes the value
    • Creates a variable
  5. When does the else block of a try statement run?

    • When an exception is caught
    • Before the try block
    • Only if there's also a finally
    • When the try block finished without any exception