Learning LibraryCore Coding LibraryTeens

Errors and debugging in Python: read the message, then trace

Code that crashes is doing you a favor: it tells you where it broke. The harder bugs are the ones that run cleanly and print the wrong number.

The big idea

Debugging is a method: read the error's type and message, find the line, and inspect the actual values until one surprises you.

See it in code

1The basics

When Python stops, the last line of the traceback names the error type and what went wrong. Here three classic mistakes are each caught with try/except, so you can read their messages side by side:

python
stock = {"TEE-SAGE-M": 12}

try:
    print(profit * 2)
except NameError as error:
    print("NameError:", error)

try:
    print("2" * 38.0)
except TypeError as error:
    print("TypeError:", error)

try:
    print(stock["tee-sage-m"])
except KeyError as error:
    print("KeyError:", error)
Run it. Three errors, three messages:
NameError: name 'profit' is not defined
TypeError: can't multiply sequence by non-int of type 'float'
KeyError: 'tee-sage-m'

NameError means a name doesn't exist yet. TypeError means an operation got the wrong kind of value, here text where a number was needed. KeyError means a dictionary key isn't there, often because of capitalization.

2A step further

Some bugs raise nothing. This forecast runs without complaint and reports a year-one figure that's far too low:

python
price = 38
units = 300
growth = 0.15

total_revenue = 0
for month in range(1, 13):
    revenue = units * price
    total_revenue = revenue
    units = round(units * (1 + growth))
print(f"Year one revenue: ${total_revenue:,}")
Run it. Plausible-looking, and wrong:
Year one revenue: $53,200

$53,200 for a year, when month one alone earns $11,400? A sanity check against a number you can estimate is the first debugging tool, before you read a line of code.

3At Evergreen

Now trace it. Print the suspect variable inside the loop for the first few passes, and look for the moment it stops behaving:

python
price = 38
units = 300
growth = 0.15

total_revenue = 0
for month in range(1, 13):
    revenue = units * price
    total_revenue = revenue
    if month <= 3:
        print(f"month {month}: revenue {revenue:,}, total so far {total_revenue:,}")
    units = round(units * (1 + growth))
print(f"Year one revenue: ${total_revenue:,}")
Run it. A temporary print exposes the bug:
month 1: revenue 11,400, total so far 11,400
month 2: revenue 13,110, total so far 13,110
month 3: revenue 15,086, total so far 15,086
Year one revenue: $53,200

The total equals each month's revenue, so it isn't accumulating. The fix is one token: total_revenue = total_revenue + revenue. Then delete the trace print.

The same idea, everywhere

The method transfers to anything. A wrong protein, a strategy that never trades, a page that won't update: state what you expected, print what you got, and narrow the gap.

Try it yourself

Apply the fix and confirm year one becomes $331,398. Then break it a different way, by moving total_revenue = 0 inside the loop, and use the same trace to diagnose it.

The common mistake

Changing several things at once and re-running. If the bug vanishes you won't know why, and if it doesn't you've added noise. Form one hypothesis, make one change, observe.

What it unlocks

Understanding types prevents most TypeErrors before they happen. For errors you expect and want to survive, such as bad user input, use try / except.

Want the simpler version? Read the Kids version →