Reading Tracebacks and Finding Bugs
45 min · Intermediate
Need help? Ask a tutor on WhatsApp (opens in a new tab)A debugging round gives you code that fails or prints the wrong thing. Python tells you a lot when it fails. Read the last line first, then the line number above it. For wrong output, trace a small input by hand and then try the edges.
Read a traceback from the bottom
A traceback is the report Python prints when an exception is not handled. It lists the chain of calls with file and line numbers, and ends with the exception type and message.
The last line has the exception type and message. The lines above it show the calls that led there, with the most recent call last. The line marked closest to the end is where Python stopped.
def avg(nums): return sum(nums) / len(nums)
def report(scores): return avg(scores)
try: report([])except ZeroDivisionError as e: print(type(e).__name__, "-", e) print("raised on line", e.__traceback__.tb_next.tb_next.tb_lineno)This prints ZeroDivisionError - division by zero, then raised on line 2. The cause is the empty list, but the failing line is inside avg.
Common exceptions and their causes
Each exception has a usual cause. TypeError means a wrong type for an operation. ValueError means the right type with a bad value. KeyError and IndexError mean a missing key or position. AttributeError means the object has no such attribute, often because it is None.
def kind(action): try: action() except Exception as e: return type(e).__name__
print(kind(lambda: int("3.5")))print(kind(lambda: "age" + 30))print(kind(lambda: {"a": 1}["b"]))print(kind(lambda: [1, 2][5]))print(kind(lambda: None.upper()))This prints ValueError, TypeError, KeyError, IndexError and AttributeError, one per line.
Bugs that print, not crash
An edge case is an input at the boundary of what the code should handle, such as an empty list, one item, duplicates, zero or negative numbers. Most bugs show up there first.
Many bugs give wrong output without any error. The usual suspects are a missing return (the function gives None), a return inside the loop that ends it early, an off-by-one range, if x == 1 or 2, and a bare except that hides the real error.
def total(prices): s = 0 for p in prices: s += p
code = 5if code == 1 or 2: print("fast")print(total([5, 10]))Example 1 · A return inside the loop
def first_even(nums): for n in nums: if n % 2 == 0: return n else: return None
print(first_even([1, 2, 4]))
def first_even_fixed(nums): for n in nums: if n % 2 == 0: return n return None
print(first_even_fixed([1, 2, 4]))None 2
What this code does
- The
elsereturns on the first odd number, so the loop never reaches 2. - Fix: move
return Noneafter the loop, so it runs only when nothing matched. - Test:
[1, 2, 4]must give 2, and[1, 3]must give None.
Example 2 · A bare except hides the cause
def parse(text): try: return int(text) except: return -1
print(parse("abc"), parse(None))
def parse_fixed(text): try: return int(text) except ValueError: return -1
print(parse_fixed("abc"))-1 -1 -1
What this code does
- The bare
exceptalso catches the TypeError forNone, hiding a real mistake. - Fix: catch only
ValueError, so other errors still show. - Test:
parse_fixed(None)must raise TypeError, not return -1.
Example 3 · A function that changes its argument
def top2(nums): nums.sort(reverse=True) return nums[:2]
scores = [70, 95, 82]print(top2(scores), scores)
def top2_fixed(nums): return sorted(nums, reverse=True)[:2]
scores = [70, 95, 82]print(top2_fixed(scores), scores)[95, 82] [95, 82, 70] [95, 82] [70, 95, 82]
What this code does
sortchanges the caller's list, a side effect nobody asked for.- Fix:
sortedreturns a new list and leaves the input alone. - Test: check the input list after the call.
Example 4 · finally always runs
def f(): try: return "try" finally: print("cleanup", end=" ")
print(f())cleanup try
What this code does
- The
returnvalue is prepared, thenfinallyruns, then the function returns. - So
cleanupis printed before the result.
This prints fast and then None. 2 is always truthy, so the if is always true, and total has no return.
Explain the fix: cause, fix, test
In a live round, say three things for each bug: the cause in one sentence, the smallest change that fixes it, and one test that would have caught it. Do not rewrite the function.
Next step
Check your understanding
12 quick questions