Chapter 7 of 13 · Free, no signup

Support Us

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.

SyntaxError

The code can't be parsed

Missing colon, quote, or bracket

Runtime error

The code ran, then hit a problem

IndexError, KeyError, TypeError

Logic bug

The code ran, but the answer is wrong

Wrong condition or formula

First ask: what did I expect, what happened instead, and what is the smallest input that reproduces it?

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.

Traceback (most recent call last):
File "main.py", line 18
print(report["top_scores"][5])
File "report.py", line 7
return scores[position]
File "report.py", line 7
return scores[position]
IndexError: list index out of range

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.

checkout()shop.py
total = order_total(cart)
try: … except KeyError: show_message()
order_total(cart)pricing.py
return sum(price_of(i) for i in cart)
price_of(item)pricing.py
return PRICES[item] # "mango" missing!
bottom of the call stack

Press Play to watch a checkout call three functions.

You don't need try/except in every function. Catch the error in the one place that knows what to do next — here, 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()
1try: risky operation
2except ValueError: handle the problem
3else: skipped (an error happened)
4finally: cleanup always runs

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()
Result: 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() fails
📁
notes.txt
resource closed

with 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.

Try to withdraw: $120
$0$150
class InsufficientFundsError(Exception):
pass
if amount > balance:
raise InsufficientFundsError(...)

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

  1. Reproduce: make the bug happen with the smallest input you can.
  2. Read the exact error: don't skip the type or message.
  3. Inspect the failing line: check the types and values it actually received.
  4. Change one thing: then rerun the same reproduction case.
  5. Keep a test: make sure the bug doesn't quietly return.
Prints can help for a quick check. A debugger is better when you need to pause execution and inspect several values at once.

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.

Python
Line-by-line view
1def safe_divide(total, count):
2 try:
3 return total / count
4 except ZeroDivisionError:
5 return 0
6
7print(safe_divide(12, 3))
8print(safe_divide(12, 0))

Names

Run the example to inspect local names.

Objects in memory

Memory snapshots appear as Python runs.

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 else for the no-error path and finally for cleanup.
  • Use with to manage files and other resources safely.
  • Raise custom exception types for meaningful application-level failures.

Finish the challenge above to complete the chapter, or

Enjoying this free lesson? Your support helps us build more free animated chapters.

Support Us