Know some Python? Write your own interactive tutorial, with runnable examples and exercises, and share it with a link. Write and share your own tutorial.

Start writing

Learn Python / Errors and Exceptions

Errors and Exceptions

Every program meets problems: a user types letters where a number was expected, a file is missing, a value is zero when you divide by it. When Python hits a problem it can't handle, it raises an exception and stops. In this lesson you'll learn to catch exceptions, so your program can respond sensibly instead of crashing.

Reading an exception

You've seen plenty of these already. Run this:

numbers = [10, 20, 30]
print(numbers[5])
Output

The last line tells you the type of exception (IndexError) and a message explaining it. The lines above show where it happened.

Some exceptions you'll meet often:

Exception Raised when… Example
ValueError a value has the right type but a bad value int("abc")
TypeError an operation gets the wrong type "5" + 5
NameError a name isn't defined prnt("hi")
IndexError a list index is out of range [1, 2][5]
KeyError a dictionary key doesn't exist {"a": 1}["b"]
ZeroDivisionError you divide by zero 10 / 0
FileNotFoundError a file doesn't exist open("missing.txt")

try and except

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

try:
    number = int("forty-two")
    print("Converted:", number)
except ValueError:
    print("That's not a valid number.")

print("The program keeps going.")
Output

Step by step:

  1. Python runs the try block. int("forty-two") raises a ValueError.
  2. The rest of the try block is skipped, so print("Converted:", ...) never runs.
  3. Python looks for an except that names that exception type. except ValueError: matches, so its block runs.
  4. The program carries on after the whole try statement.

If nothing goes wrong, the except block is skipped entirely. And except only catches the type it names. If the try block raised a different exception, say a NameError from a typo, except ValueError wouldn't catch it, and the program would stop with the usual error message.

Keep the try block small: put in only the lines that might fail. Then the except can't accidentally hide a problem in some other line.

Getting the error message

Use as to get the exception object. Printing it shows the message:

try:
    result = 10 / 0
except ZeroDivisionError as e:
    print("Something went wrong:", e)
Output

as e stores the exception in a variable called e (any name works, but e is the convention). Printing it gives just the message, division by zero, without the long traceback.

Handling different exceptions

Use several except blocks to handle each kind of problem differently:

def safe_divide(a, b):
    try:
        return a / b
    except ZeroDivisionError:
        return "Can't divide by zero"
    except TypeError:
        return "Both values must be numbers"

print(safe_divide(10, 2))
print(safe_divide(10, 0))
print(safe_divide(10, "two"))
Output

Python checks the except blocks from top to bottom and runs only the first one that matches. 10 / 0 matches ZeroDivisionError; 10 / "two" matches TypeError. return inside try works as usual: when nothing fails, a / b is returned straight away.

To handle several types the same way, group them in a tuple:

values = ["12", "abc", None]

for v in values:
    try:
        print(int(v) * 2)
    except (ValueError, TypeError) as e:
        print(f"Skipping {v!r}: {e}")
Output

int("abc") raises a ValueError and int(None) raises a TypeError; both go to the same block. (!r in an f-string shows the value the way you'd write it in code, so strings get their quotes: 'abc' rather than abc.)

Be specific. A bare except: or except Exception: catches everything, including typos and bugs you'd want to know about. Catch only the exceptions you expect and know how to handle.

else and finally

A try statement can have two more optional parts:

  • else runs only if no exception happened.
  • finally runs always, whether there was an exception or not. It's used for clean-up.
def parse_age(text):
    try:
        age = int(text)
    except ValueError:
        print(f"  {text!r} is not a number")
    else:
        print(f"  Age is {age}")
    finally:
        print("  (finished checking)")

parse_age("36")
parse_age("thirty")
Output
parse_age("36") parse_age("thirty")
try int("36") works int("thirty") raises ValueError
except ValueError skipped runs
else runs: Age is 36 skipped
finally runs runs

Why not just put the print at the end of the try block? Code in else is not protected by the except, so an unexpected error there won't be mistaken for the one you're handling. finally runs even when the try block uses return or an exception goes uncaught, which is why it's used for clean-up, such as closing a file.

Validating user input

Combining a while loop with try gives you a robust way to ask for a number. Run it and try typing some letters first:

while True:
    try:
        age = int(input("How old are you? "))
        break
    except ValueError:
        print("Please type a whole number.")

print(f"Next year you'll be {age + 1}.")
Output

The break is inside the try, right after the conversion. If int() fails, Python jumps to except before reaching break, prints the message, and the loop asks again. Only a successful conversion reaches break and leaves the loop.

Raising exceptions

You can raise exceptions yourself with raise. It's the right way for a function to say "I can't work with this input":

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

print(set_age(30))
print(set_age(-5))
Output

raise stops the function on the spot, like return, but instead of giving back a value it sends an exception up to the caller. Here nothing catches it, so the program stops with ValueError: Age can't be negative. The message in the brackets is what appears after the exception type. Choose the type that fits: ValueError for a bad value, TypeError for the wrong kind of value.

Whoever calls the function can then decide how to handle it:

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

try:
    set_age(-5)
except ValueError as e:
    print("Invalid input:", e)
Output
Going deeper: assert: checks for programmers optional

assert condition, "message" raises an AssertionError if the condition is false. It's a quick way to write down something that must be true at that point in your code, and to catch your own mistakes early:

def average(numbers):
    assert len(numbers) > 0, "average() needs at least one number"
    return sum(numbers) / len(numbers)

print(average([4, 8]))
print(average([]))
Output

Use assert for things that should never happen if your code is correct. Don't use it to check user input: Python can be run with asserts switched off (python -O), and then the check silently disappears. For input, use if and raise as shown above.

Your own exception types

For bigger programs you can define your own exception types. It takes two lines (you'll learn what class means in Classes and Objects):

class InsufficientFundsError(Exception):
    pass

def withdraw(balance, amount):
    if amount > balance:
        raise InsufficientFundsError(f"Balance {balance} is less than {amount}")
    return balance - amount

try:
    withdraw(50, 80)
except InsufficientFundsError as e:
    print("Declined:", e)
Output

Exercises

Exercise 1: Safe conversion

Write to_int(text) that returns the text converted to an int, or None if it can't be converted.

def to_int(text):
    pass

print(to_int("42"))
print(to_int("hello"))
Output

Exercise 2: Safe dictionary lookup

Look up each product's price and print it like apple: 0.5. If a product is missing, catch the KeyError and print <name>: not found.

prices = {"apple": 0.5, "bread": 2.2}
shopping = ["apple", "milk", "bread"]
Output

Exercise 3: Sum the valid numbers

Add up only the entries that can be converted to numbers, skipping the rest. It should print Total: 37.5.

entries = ["10", "abc", "7.5", "", "20"]
Output

Exercise 4: Raise an error

Write percentage(part, whole) that returns part / whole * 100. If whole is 0, raise a ValueError with the message whole must not be zero. Then call percentage(5, 0) inside try/except and print Error: followed by the message.

def percentage(part, whole):
    pass

print(percentage(25, 200))
Output

Exception handling at a glance

A quick reference for later.

Code What it does
try: Runs code that might raise an exception
except ValueError: Handles one type of exception
except ValueError as e: Handles it, and stores the exception in e so you can print its message
except (ValueError, TypeError): Handles any of several types with the same code
except Exception: Handles almost any error; use sparingly, it can hide bugs
else: Runs only if the try block raised no exception
finally: Always runs, error or not; good for clean-up
raise ValueError("message") Raises an exception yourself
raise On its own inside except: re-raises the exception you're handling
class MyError(Exception): pass Defines your own exception type
assert condition, "message" Raises AssertionError if the condition is false; for catching your own mistakes

Summary

  • An exception stops the program unless it is caught. The last line of the traceback names it.
  • try: runs risky code, and except SomeError: handles that error.
  • except SomeError as e gives you the error message.
  • else runs if nothing went wrong, and finally always runs.
  • Catch specific exceptions, not everything.
  • raise ValueError("message") signals a problem from your own code.

Share this tutorial

Know something worth teaching? Write your own tutorial with runnable examples and share it.