Learning LibraryCore Coding LibraryTeens

Errors and Debugging in Python: read the traceback

Debugging isn't guesswork — it's a method. Python's error messages are precise, and a few well-placed prints turn a mysterious bug into an obvious one.

The big idea

A traceback names the error type and the exact line; print-debugging shows you where a value stops matching your expectation.

See it in code

1The basics

The first debugging move is to make a value reveal itself. Printing it — and its type — catches bugs that hide in plain sight, like + concatenating strings instead of adding numbers:

python
total = "3" + "1"
print("total is", total)
print("type is", type(total))
Run it — the print exposes what really happened:
total is 31
type is <class 'str'>

You expected 4, but got "31" — a string. Printing the value and its type is often all it takes to see a bug that the code itself never complained about.

2A step further

When the wrong value comes out of a loop, print the running state each pass. That turns a mystery total into a visible, step-by-step trail:

python
total = 0
for v in [3, 1, 4]:
    total += v
    print(f"added {v}, total now {total}")

print("final:", total)
Run it — each step is visible, so a wrong one would stand out:
added 3, total now 3
added 1, total now 4
added 4, total now 8
final: 8

Every pass reports what it added and where the total stands. If reality diverged from your expectation, you'd see the exact step it happened.

3In our world

Wrap that same running total in a function and number each pass with enumerate. When a total comes out wrong, this trace shows precisely where things went sideways:

python
def moving_sum(values):
    total = 0
    for i, v in enumerate(values):
        total += v
        print(f"  step {i}: added {v}, total={total}")
    return total

print("Sum:", moving_sum([3, 1, 4, 1]))
Run it — every step is visible, so a wrong one would stand out:
  step 0: added 3, total=3
  step 1: added 1, total=4
  step 2: added 4, total=8
  step 3: added 1, total=9
Sum: 9

Same instrumented loop, now inside a function with enumerate numbering the steps. If the total were wrong, this trace would pinpoint the exact one. Read tracebacks the same deliberate way — start at the bottom, which names the error and the line that raised it, then read up the call stack if you need to see how you got there.

The same idea, everywhere

Two error types cover most early bugs. NameError means a name doesn't exist — usually a typo or a missing import. TypeError means an operation hit the wrong type — like adding a string to an int. IndexError and KeyError mean you reached past a list or into a missing dict key. Each names the fix.

Try it yourself

Break it on purpose: change total += v to total += w and read the NameError. Then pass moving_sum([3, "1"]) and watch a TypeError appear when it tries to add a string. Read the last line first each time.

The common mistake

Reading a traceback top-down and giving up. The most useful line is the last one — it states the error type and message. The lines above are the trail of calls that led there; scan them only after you've read the bottom.

What it unlocks

Debugging skill pairs with print output, and graduates into try / except for handling errors you expect.

Want the simpler version? Read the Kids version →