Python Mastery · Chapter 7 · ~18 min · Intermediate
Errors & Debugging like a Pro
Errors aren't a dead end. Learn to read Python's clues, handle expected problems safely, and find bugs without guesswork.
- Read any traceback from the bottom up
- Catch specific errors, not everything
- Use else / finally / with correctly
- Debug with a calm 5-step routine
1Errors are clues, not failures
Python stops when it reaches a problem it can't safely continue from. The error type and message tell you what kind of assumption was wrong; the traceback tells you where it happened and how execution got there.
The code can't be parsed
Missing colon, quote, or bracket
The code ran, then hit a problem
IndexError, KeyError, TypeError
The code ran, but the answer is wrong
Wrong condition or formula
2Read a traceback from the bottom up
A traceback is a path of function calls. Start at the bottom to find the exception and failing line, then move upward to see who called it. Pick an error to inspect the stack frames.
Where should you look FIRST in a traceback?
3Watch an error bubble up the call stack
Real story: a customer adds a mango to their cart, but the shop's price list has no mango. Remember the call stack from Chapter 5? When an error happens, Python throws away the top box and hands the error to the box below — again and again — until some function catches it with try/except. If nobody does, the program crashes.
Press Play to watch a checkout call three functions.
checkout() can tell the customer politely.4Handle errors with try / except
Use try around the small operation that may fail. Catch the error you can recover from, and show a useful next step. Don't use exceptions to hide bugs you don't understand.
try:
result = risky_step()
except ValueError as error:
recover(error)
else:
celebrate(result)
finally:
close_file()Choose a path, then advance one line at a time.
else runs only when no exception happened. finally runs either way, so reserve it for cleanup.Which block runs whether or not an error happened?
5Catch the right exception
Python exceptions form a family tree. A broad parent like Exception can catch many different problems. If it comes first, narrower handlers below it never get a chance.
try:
do_work()
except Exception:
log("something failed")
except FileNotFoundError:
recover_specific_case()Exception catches FileNotFoundError first, so the specific handler is unreachable. Put specific exceptions before general ones.except Exception comes BEFORE except KeyError. A KeyError happens. Which handler runs?
6Prevent cleanup bugs with with
Closing a file manually is easy to forget, especially when an exception interrupts the function. A context manager takes responsibility for cleanup when its block exits.
with open("notes.txt") as file:
text = file.read()
# file closes here — even if read() failswith makes resource cleanup reliable. Files close automatically; locks and database connections can use the same pattern.
7Give expected failures a clear name
When your application has a meaningful failure case, define a custom exception. Callers can then handle it explicitly while unexpected errors remain visible.
A named exception tells callers what went wrong and lets them handle that case without guessing from an error message.
class InvalidQuantityError(Exception):
pass
if quantity < 1:
raise InvalidQuantityError("quantity must be at least 1")8A practical debugging routine
- Reproduce: make the bug happen with the smallest input you can.
- Read the exact error: don't skip the type or message.
- Inspect the failing line: check the types and values it actually received.
- Change one thing: then rerun the same reproduction case.
- Keep a test: make sure the bug doesn't quietly return.
9Challenge: Debugging detective
Five short questions on tracebacks, exception handling, and cleanup. Get all five right to complete Chapter 7.
Errors & Debugging Like a Pro · code lab
Edit the example, run it with Python, or step through the lines.
1def safe_divide(total, count):2 try:3 return total / count4 except ZeroDivisionError:5 return 067print(safe_divide(12, 3))8print(safe_divide(12, 0))
Names
Objects in memory
Python output
Run the code to see its output.
1.In a traceback, where is the line that actually raised the error?
2.Which handler order is safest?
3.When does the finally block run?
4.What does a with statement help guarantee?
5.Why create a custom exception?
Try the checkout example in the Python Playground (code preloaded).
10Recap
- Read the traceback's final frame to locate the failing line, then trace calls upward.
- Catch specific errors you can recover from; put a broad fallback last.
- Use
elsefor the no-error path andfinallyfor cleanup. - Use
withto manage files and other resources safely. - Raise custom exception types for meaningful application-level failures.
Finish the challenge above to complete the chapter, or