promptdojo_

Raising errors — making your code fail loudly on purpose — step 1 of 9

Raising errors — telling Python to crash on purpose

So far every exception you've seen was raised by Python itself: a bad int(...), a missing dict key, an out-of-range index. But you can also raise your own. The keyword is raise, and it's how you turn a quiet "the inputs to this function are nonsense" into a loud, traceback-able crash that points at the exact line that's wrong.

This is the move that separates code that hides bugs from code that finds them for you. Cursor reaches for it inconsistently — sometimes right, often missing. Reading AI code, the question to ask at the top of every function is: what inputs make this function meaningless, and does it raise for them or silently produce garbage?

The mental model

raise is the inverse of try/except. Where try says "if something goes wrong, here's what to do," raise says "something is wrong, here's what to throw." The shape:

raise ValueError("amount must be positive")

Three parts: the keyword raise, an exception class (ValueError), and — usually — a string explaining what's wrong. Python wraps the message in an instance of the class and propagates it up the call stack the same way an automatic exception would. Anyone with a matching except catches it. Nobody catches it, the program crashes with a traceback.

When the message is good, the traceback alone tells the next developer (or future-you) exactly what went wrong without opening the file. When the message is "error" or "invalid", you get a useless traceback that forces you to re-read the function. Spend the extra five seconds on a real message.

A worked example

The editor on the right has a payment function with input validation:

def charge(amount):
    if amount <= 0:
        raise ValueError(f"amount must be positive, got {amount}")
    print(f"charging ${amount}")

charge(50)
charge(-10)

charge(50) passes the check (50 is greater than 0), so it prints charging $50 and returns. charge(-10) fails the check on line 2. The raise fires immediately. Python builds a ValueError instance with the message amount must be positive, got -10 and propagates it up. There's no try around the call site, so the program crashes and you see a clean traceback that names the offending value.

That last detail — "got -10" — is what makes a raised exception useful. The class tells you the kind of failure (a value is invalid). The message tells you which value, what it was, and why it was wrong. Six words. Unforgettable when you're debugging at 11pm.

Where AI specifically gets this wrong

Two patterns to flag in code Cursor writes you.

One: silent guards. AI loves to write if amount <= 0: return None instead of raise. Now the function "works" for negative inputs — it returns nothing, and the caller has no idea anything went wrong. Three function calls later, something else explodes for a reason that has nothing to do with the real bug. raise would have caught it on line two of the original function.

Two: useless messages. raise ValueError("error") and raise ValueError() are both legal Python and both useless. The exception class alone is barely more informative than print("something broke"). Always include a message that names which input failed and what its value was.

Run the editor. The first call works. The second crashes with a traceback that points at line 3 — the raise itself — and tells you exactly what was wrong with the input.

read, then continue.