Error Handling and Debugging¶
By the end of this lesson you'll be able to:
- Identify the three types of Python errors: syntax, runtime, and logic
- Read a Python traceback and find exactly where the problem is
- Use
try/exceptto catch errors and keep your program running - Use
raise,assert, and strategicprint()calls to find and report bugs
Every programmer makes mistakes. The skill isn't avoiding errors — it's finding and fixing them quickly.
Welcome to Chapter 23!
Errors aren't failures — they're clues! Python's error messages are actually very helpful once you know how to read them.
Today we'll turn scary red text into useful information.
Let's code it together!
Three Types of Errors¶
A syntax error is caught before the program runs. Python can't parse the code at all.
A missing colon after if, bad indentation, or a mismatched parenthesis causes a syntax error.
A runtime error (also called an exception) happens while the program is running. The program starts, then crashes when it hits the bad line.
A logic error is the trickiest — the program runs without crashing, but produces the wrong result. Python can't detect logic errors; you have to find them yourself.
| Error type | When detected | Example |
|---|---|---|
| Syntax error | Before running | Missing : after if |
| Runtime error | While running | int("hello") |
| Logic error | After running | Off-by-one in a loop |
Common Exception Types¶
When a runtime error occurs, Python raises an exception — a named error type.
| Exception | Cause |
|---|---|
NameError |
Variable name doesn't exist |
TypeError |
Wrong type — e.g. adding string to integer |
ValueError |
Right type but bad value — e.g. int("hello") |
IndexError |
List index out of range |
KeyError |
Dictionary key doesn't exist |
ZeroDivisionError |
Dividing by zero |
AttributeError |
Method doesn't exist on the object |
FileNotFoundError |
File can't be found |
Reading a Traceback¶
When Python crashes, it prints a traceback — a trace of exactly where things went wrong.
1 2 3 4 | |
Read from the bottom up:
1. The last line gives the exception type and message: ZeroDivisionError: division by zero
2. Above it, the exact code that crashed: result = 10 / 0
3. File and line number so you know where to look: line 5
What Do You Think Will Happen?
The code below has a bug. Which line will crash, and what exception type will appear?
Make your guess — then run it!
Were you right? IndexError: list index out of range — index 5 doesn't exist in a 3-item list.
try/except Blocks¶
A try/except block lets your program catch an error and handle it gracefully instead of crashing.
1 2 3 4 | |
Structure:
1. try: — put the risky code here
2. except ErrorType: — put the recovery code here; runs only if that error occurs
Catching Specific Exceptions¶
Catch multiple exception types with multiple except clauses:
1 2 3 4 5 6 7 8 | |
Catching specific exceptions is better than a bare except: — it prevents hiding bugs you didn't intend to catch.
else and finally Clauses¶
Two optional extra clauses make try/except even more powerful:
else:runs only if thetryblock completed without any exceptionfinally:runs always — whether or not an exception occurred; perfect for cleanup
1 2 3 4 5 6 7 8 9 | |
The raise Statement¶
Use raise to trigger an exception yourself when input violates a rule:
The assert Statement¶
assert condition, "message" checks that something you believe is true actually is true.
If not, it raises AssertionError. Use it for internal checks during development.
1 2 3 4 | |
Validating Input Before Casting¶
input() always returns a string — even when you ask for a number. Casting it straight to int() crashes if the text isn't a valid number:
1 2 3 4 5 | |
Typing a word instead of a number raises ValueError: invalid literal for int() with base 10. You can catch bad input before it crashes by checking it first with the string method .isdigit(), then using assert to stop with a clear message if the check fails:
1 2 3 4 5 6 | |
.isdigit() returns True only if every character in the string is a digit, so it catches bad input before int() ever runs.
Debugging with print()¶
The simplest debugging tool is a strategic print() statement. Insert them to see what your program is doing at each step:
1 2 3 4 5 6 7 | |
Remove debug prints once the problem is fixed.
Using Search Engines to Debug
When you see an error you don't recognize, copy the last line of the traceback and paste it into a search engine — add "Python" at the start.
The Python community is enormous and someone has almost certainly had the same error before!
Reading solutions on Stack Overflow is how professional programmers debug every day.
Learning Check¶
Your Turn — Wrap the Dangerous Code
The program below crashes when the user enters something that isn't a number.
Wrap the int() call in a try/except ValueError block so it prints a friendly message instead!
Experiments¶
Try these changes. Predict what will happen first, then run it to check!
-
Write code that deliberately triggers a
NameErrorby using an undefined variable, then catch it. You'll know it worked when the error is caught and a friendly message appears instead of a crash. -
Write a function
safe_index(lst, i)that returnslst[i]orNoneifiis out of range. You'll know it worked whensafe_index([1,2,3], 10)returnsNonewithout crashing. -
Add an
elseclause to atry/exceptthat prints"Success!"only when no error occurs. You'll know it worked when valid input shows "Success!" and invalid input doesn't. -
Add a
finallyclause that always prints"Calculation complete."no matter what. You'll know it worked when the message appears for both valid and invalid input. -
Use
assertto verify that a list has exactly 3 items. Test with lists of different lengths. You'll know it worked when a 2-item list raisesAssertionErrorand a 3-item list passes.
Debugging Champion!
You can now read tracebacks, catch exceptions, raise your own errors, and debug like a professional!
These skills transform error messages from obstacles into tools.
That's a huge step forward. Let's keep coding!