Skip to content

Error Handling with Try/Except

💡
Before you start

You need Python installed, and to know how to run a .py file. If python3 --version in a terminal prints a version number, you are ready. If it does not — or if you have never opened a terminal — do Getting Started with Python first; it installs Python and runs your first program, and takes about fifteen minutes. Nothing else is needed: no account, no payment, no extra software.

What Are Exceptions?

Exceptions are errors that occur during program execution. Without handling, they crash your program with a traceback message. Error handling lets you anticipate problems and respond gracefully.

# This crashes the program
number = int("hello")    # ValueError: invalid literal for int()

# This file might not exist
file = open("missing.txt")   # FileNotFoundError

Try/Except Basics

Wrap risky code in a try block and handle errors in except:

try:
    number = int(input("Enter a number: "))
    print(f"You entered: {number}")
except ValueError:
    print("That's not a valid number!")

If the code in try raises a ValueError, Python jumps to the except block instead of crashing.

Common Exception Types

# ValueError — wrong value type
int("hello")

# TypeError — wrong operation for type
"hello" + 5

# ZeroDivisionError — division by zero
10 / 0

# FileNotFoundError — file doesn't exist
open("nonexistent.txt")

# KeyError — dictionary key not found
d = {"a": 1}
d["b"]

# IndexError — list index out of range
lst = [1, 2, 3]
lst[10]

# NameError — variable not defined
print(undefined_var)

# AttributeError — object has no such attribute
"hello".nonexistent_method()

Catching Multiple Exceptions

# Separate handlers for different errors
try:
    value = int(input("Enter a number: "))
    result = 100 / value
    print(f"Result: {result}")
except ValueError:
    print("Please enter a valid number.")
except ZeroDivisionError:
    print("Cannot divide by zero!")

# Catch multiple types in one handler
try:
    data = process_input()
except (ValueError, TypeError, KeyError) as e:
    print(f"Input error: {e}")

The Full Try/Except Structure

try:
    file = open("data.txt", "r")
    content = file.read()
    number = int(content)
except FileNotFoundError:
    print("File not found!")
    number = 0
except ValueError:
    print("File doesn't contain a valid number!")
    number = 0
else:
    # Runs ONLY if no exception occurred
    print(f"Successfully read number: {number}")
finally:
    # ALWAYS runs, whether or not an exception occurred
    print("Operation complete.")
💡
When to use else vs finally

else runs only on success — use it for code that should only execute if no errors occurred. finally always runs — use it for cleanup tasks like closing connections or releasing resources.

Accessing Exception Details

try:
    result = 10 / 0
except ZeroDivisionError as e:
    print(f"Error type: {type(e).__name__}")   # ZeroDivisionError
    print(f"Error message: {e}")                # division by zero

Raising Exceptions

Use raise to throw your own exceptions:

def set_age(age):
    if age < 0:
        raise ValueError("Age cannot be negative")
    if age > 150:
        raise ValueError("Age seems unrealistic")
    return age

try:
    user_age = set_age(-5)
except ValueError as e:
    print(f"Invalid age: {e}")    # Invalid age: Age cannot be negative

Custom Exception Classes

class InsufficientFundsError(Exception):
    def __init__(self, balance, amount):
        self.balance = balance
        self.amount = amount
        super().__init__(
            f"Cannot withdraw ${amount}. Balance: ${balance}"
        )

def withdraw(balance, amount):
    if amount > balance:
        raise InsufficientFundsError(balance, amount)
    return balance - amount

try:
    new_balance = withdraw(100, 150)
except InsufficientFundsError as e:
    print(e)           # Cannot withdraw $150. Balance: $100
    print(e.balance)   # 100
    print(e.amount)    # 150

Best Practices

⚠️
Never use bare except:

Catching all exceptions hides bugs and makes debugging impossible. Always catch specific exception types.

# BAD — catches everything, hides bugs
try:
    do_something()
except:
    pass

# BAD — too broad
try:
    do_something()
except Exception:
    pass

# GOOD — catch specific errors
try:
    do_something()
except (ValueError, KeyError) as e:
    print(f"Known error: {e}")

# GOOD — catch broad only when logging
try:
    do_something()
except Exception as e:
    print(f"Unexpected error: {e}")
    raise    # Re-raise so the error isn't silently swallowed

Practical Examples

Safe User Input

def get_integer(prompt, min_val=None, max_val=None):
    while True:
        try:
            value = int(input(prompt))
            if min_val is not None and value < min_val:
                print(f"Must be at least {min_val}")
                continue
            if max_val is not None and value > max_val:
                print(f"Must be at most {max_val}")
                continue
            return value
        except ValueError:
            print("Please enter a valid number.")

age = get_integer("Enter your age: ", min_val=0, max_val=150)

Safe File Reader

def read_config(filename):
    try:
        with open(filename, "r") as file:
            config = {}
            for line in file:
                line = line.strip()
                if "=" in line and not line.startswith("#"):
                    key, value = line.split("=", 1)
                    config[key.strip()] = value.strip()
            return config
    except FileNotFoundError:
        print(f"Config file '{filename}' not found, using defaults.")
        return {}
    except PermissionError:
        print(f"No permission to read '{filename}'.")
        return {}

settings = read_config("app.conf")

Now Do It Yourself: Five Steps

An exception is what Python raises when it cannot do what you asked. Handling one is the difference between a program that stops dead in front of a user and one that says something useful. You will cause a crash, catch it, read it, and build the full shape. Every output below is exactly what Python printed.

1
Cause a crash and read what it tells you

Go: open a terminal, run cd ~, and create a file called safe.py.

Do: type this one line and run python3 safe.py. It is meant to fail.

print(int("abc"))

You should see: a traceback ending ValueError: invalid literal for int() with base 10: 'abc'. Two pieces of information are in that line: the type is ValueError, which is what you will catch, and the message explains it in words.

If not: if you see NameError, you left the quotes off abc and Python read it as a variable name. The type on the last line always tells you which kind of problem it is — read that before anything else in a traceback.

2
Catch it, so the program keeps going

Go: same file, replace the contents.

Do: type these four lines and run it.

try:
    n = int("abc")
except ValueError:
    print("that was not a number")

You should see: that was not a number, and no traceback. The program finished normally. Everything that might fail goes in the try block; what to do about it goes in except.

If not: if you still get the traceback, check the exception name matches. Catching KeyError here changes nothing at all — the ValueError sails straight past it and crashes exactly as before, because an except only catches the type it names.

3
Keep the detail instead of throwing it away

Go: same file, replace the contents.

Do: type these four lines and run it.

try:
    n = int("abc")
except ValueError as e:
    print("failed:", e)

You should see: failed: invalid literal for int() with base 10: 'abc'. as e gives you the exception object, and printing it gives you the message Python would have shown. A handler that swallows the reason is often worse than no handler at all.

If not: if you print e and get something unhelpfully short, that is genuinely all the exception carried — not every one has a long message. Add your own context: print("could not read the age field:", e) tells whoever reads the log where it happened.

4
Add else and finally — the two halves people skip

Go: same file, replace the contents.

Do: type these eight lines and run it. Note the value is "42" this time, so it succeeds.

try:
    n = int("42")
except ValueError:
    print("bad")
else:
    print("got", n)
finally:
    print("always runs")

You should see: got 42 then always runs. else runs only when nothing was raised; finally runs either way. Change "42" to "abc" and you get bad then always runs — the finally is the point, it is where you close things you opened.

If not: SyntaxError means your blocks are out of order. The sequence is fixed: try, then except, then else, then finally. You may leave any of the last three out, but you cannot reorder them.

5
Use it on real input, the way you actually will

Go: same file, replace the contents.

Do: type these seven lines and run it. Answer the question with a word rather than a number, on purpose.

while True:
    try:
        age = int(input("How old are you? "))
        break
    except ValueError:
        print("Please type digits only, for example 25.")
print("Thank you. Next year you will be", age + 1)

You should see: your message repeating until you type digits, then the final line. This is the standard shape for anything a person types: loop, try, break on success.

If not: if it loops forever even with digits, the break is missing or misindented — it must be inside try, after the conversion, so it is only reached when the conversion worked. Press Ctrl + C to escape.

🎉
Check yourself before moving on

Without scrolling up: you wrap a conversion in try and catch KeyError, but it still crashes with ValueError. Why? Answer: an except only catches the type it names. Catch ValueError, or the right type for the error you actually saw — which is always on the last line of the traceback.

Now do it without the page: take the file reader from the file-io tutorial and make it print "no notes yet" instead of crashing when notes.txt does not exist. You will catch FileNotFoundError — the same shape as step 2.

Summary

  • try/except catches and handles exceptions instead of crashing
  • Always catch specific exception types, not bare except:
  • else runs on success; finally always runs (cleanup)
  • raise throws exceptions; create custom classes for domain-specific errors
  • Use as e to access the error message and details
  • Common types: ValueError, TypeError, FileNotFoundError, KeyError, IndexError
🎉
Error handling mastered!

Your programs are now resilient to unexpected inputs and failures. Next up: classes and objects — the foundation of object-oriented programming in Python.