read the failure
when python crashes, it tells you exactly what happened and where. most non-engineers panic at the wall of text — and ai 'fixes' the crash by hiding it. learn to read the failure, then catch only what's worth catching.
Cursor wrote this and it crashed — now what?
You ask Cursor to add a feature. Cursor writes 40 lines of confident-looking Python. You hit Run. The terminal explodes into a wall of text. Lines and lines of file paths, line numbers, function names, indented arrows. The actual error message is buried at the bottom in red.
Most non-engineers panic at this. They scroll up looking for the "real" error and don't find it. They paste the whole thing back to Cursor and ask it to fix. Sometimes that works, more often the model thrashes on it for three turns and produces a different broken version — or worse, "fixes" the crash by wrapping it in code that hides it.
The trick is that the wall of text isn't a wall of text. It's a structured report. And the hiding move has a name — try/except: pass — and it's the most-shipped AI error-handling mistake there is. This chapter teaches you both halves: read the failure, and catch only what you know how to handle.
The mental model: a traceback is a map. Read bottom-up.
A Python traceback is a stack of function calls, ordered top-to-bottom, with the actual crash at the very bottom. Read it bottom-up.
Traceback (most recent call last):
File "app.py", line 42, in <module>
main()
File "app.py", line 38, in main
process_users(users)
File "app.py", line 22, in process_users
user_id = user["id"]
KeyError: 'id'
The bottom line — KeyError: 'id' — is what went wrong. The lines above it are the chain of calls that led there. Read bottom-up: "I tried to look up the key 'id', on a dict, in process_users, line 22. I got there because main called process_users on line 38. I got there because something called main on line 42 of app.py."
That's the entire trick. Bottom is the bug. Going up tells you how the program got to the bug. Most non-engineers read tracebacks top-down (because that's how you read everything else) and feel lost in the middle. Reading bottom-up is the unlock.
The second model: a guard rail, not a blanket
Some failures aren't bugs — the file isn't there, the API blinks, the user pastes garbage. try/except is how you keep the program alive long enough to log what went wrong and recover. Used right, it's a guard rail: you wrap a specific operation that can fail, you catch the specific exception you know how to handle, and you do something useful with it.
try:
response = httpx.get(url, timeout=5)
response.raise_for_status()
except httpx.TimeoutException:
log("API timeout, retrying in 30s")
sleep(30)
# retry...
except httpx.HTTPStatusError as e:
log(f"API returned {e.response.status_code}")
raise
Read that as: try this network call. If it times out, I know what to do — log and retry. If the server returned a bad status, I know what to do — log and re-raise so the caller sees it. Anything else, I don't know how to handle, so let it bubble up.
That last part is the key. You catch the exceptions you know how to handle, and you let the rest bubble up. The wrong move — and the one AI ships fluently — is try: ... except: pass, which catches every exception, including the ones you didn't anticipate, and silently drops them. Bugs become invisible. Two weeks later your prod is corrupting data and no error log mentions it.
What this chapter covers in three lessons
Lesson 1: A traceback is a map — read bottom-up. What every line in a traceback means, the bottom-up reading order, and the five error classes AI ships constantly (NameError, TypeError, KeyError, AttributeError, IndexError — plus the ValueError/ZeroDivisionError twins). By the end you'll diagnose the bug from the class alone, before you even read the message.
Lesson 2: try/except — the patterns AI misuses. The shape, which block runs when, and the two flagship AI mistakes: the bare except: that swallows bugs whole, and except Exception: when the right answer is one specific class. You'll fix both and write a multi-except function that lets real bugs crash loudly.
Lesson 3: Fail loudly — raise and the silent swallow. When to raise (never silently swallow; always either handle or re-raise), messages that name the bad value, and the fix for the swallowed-error bug that costs teams days. Ends with the habit that outlasts this course: making your own AI assistant explain any error three ways, then verifying the diagnosis before you accept the fix.
What AI specifically gets wrong here
except: passeverywhere. The flagship bug. Catching everything and dropping it on the floor makes the immediate error go away — and resurfaces it three steps later with a misleading message. Lessons 2 and 3 both drill fixing one.- Catching too broad. AI writes
except Exceptionwhenexcept FileNotFoundErroris the right answer. The broad catch welds together failure modes that need different handling and hides the bugs you'd rather see. Lesson 2 covers it. - Reading the traceback top-down. Paste a traceback into a prompt and the model sometimes confuses which function is the source of the error vs which is the caller — then "fixes" the wrong function. Lesson 1 teaches you to check its work.
- Silent guards and useless raises.
if amount <= 0: return Noneinstead ofraise, andraise Exception("error")instead ofraise ValueError("price must be > 0, got -3"). Lesson 3 fixes both habits.
What you'll be able to do at the end
Three lessons, 24 steps. By the end you'll be able to:
- Read any Python traceback bottom-up and identify in 30 seconds: what error type, what line, what caused it.
- Recognize the five common crash classes and predict the bug from the class alone.
- Read any
try/exceptblock and tell whether it's helpful or a silent-failure trap. - Write error handling that catches what you can fix and lets everything else bubble up.
- Make failures loud with
raiseand a message that names the bad value.
Real I/O fails — file reads, network calls, API requests. The read-the-pipeline lab and everything in the build track lean on this hard. This chapter is the difference between a PM who debugs alongside their engineer and a PM who hands every error over for someone else to fix.
Press Start chapter below.