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:
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
| Exception | Typical cause |
|---|---|
ValueError | Right type, wrong value: int("two") |
TypeError | Wrong type: "5" + 5 |
NameError | A variable name that doesn't exist |
IndexError | A list position that doesn't exist |
KeyError | A dictionary key that doesn't exist |
ZeroDivisionError | Dividing by zero |
FileNotFoundError | Opening 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:
try:
age = int(input("Age: "))
except ValueError:
print("Please type a whole number.")- If the code in
tryruns without problems, theexceptblock is skipped. - You can have several
exceptblocks 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
tryblocks 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:
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:
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 - quantityCallers 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:
try:
port = int(setting)
except ValueError as error:
raise ValueError(f"Port must be a number, got {setting!r}") from errorPython 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.
trywraps risky code;except SomeErrorhandles that error type.- Catch specific exceptions, and keep the
tryblock small. elseruns when nothing failed;finallyalways 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.
Asking until the input is valid
Here the user typed ten, then -3, then 12.
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")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 ticketsLine-by-line explanation
- Line 1Keep asking until the input is valid.
- Line 3int() raises ValueError for text that isn't a whole number.
- Lines 4 to 5Our own rule raises the same type of error, with our own message.
- Lines 6 to 7Both kinds of problem are handled in one place.
- Lines 8 to 9else only runs when nothing went wrong, so the loop ends.
Handling different errors differently
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))1.25
We don't sell cake
Need at least one personLine-by-line explanation
- Line 5Two different things can go wrong on this line.
- Lines 6 to 9Each exception type gets its own helpful message.
finally for clean-up
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)Opening connection
Closing connection
Report: sales
Opening connection
Closing connection
Error: Report name is emptyLine-by-line explanation
- Lines 7 to 8finally runs before the function returns, and also when an error is raised.
- Lines 11 to 14The caller decides how to handle the error raised inside the function.
Common Python Error Handling Mistakes
Catching every exception
try:
total = sum(prcies) # typo
except:
total = 0try:
total = sum(prices)
except TypeError:
total = 0The bare except hides the NameError caused by the typo, so the bug goes unnoticed.
Wrapping too much code
try:
data = load_data()
report = build_report(data)
send_email(report)
except ValueError:
print("Something went wrong")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
try:
first = items[0]
except IndexError:
first = Nonefirst = items[0] if items else NoneWhen 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.
Complete the code so a bad number is handled instead of crashing.
:
n = int("abc")
ValueError:
print("Not a number")
What does this print?
try:
print(10 / 0)
except ZeroDivisionError:
print("Oops")
What does this print?
try:
x = int("7")
except ValueError:
print("A")
else:
print("B")
finally:
print("C")
Which exception does {"a": 1}["b"] raise?
Why is except: with no exception type usually a bad idea?
Python Error Handling Quiz
5 questions to check what you learned.
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
Which exception does
int("hello")raise?- TypeError
- NameError
- ValueError
- KeyError
When does a
finallyblock run?- Only when there's an error
- Always
- Only when there's no error
- Never inside functions
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
When does the
elseblock 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