catching the right error — not 'anything that goes wrong'
Two shortcuts you'll see in real AI code
The "one except per class" pattern from the last step is the safe default, but you'll see two other shapes constantly in code Cursor writes. Both are useful, and both have a specific time to use them.
The tuple form: catch several classes the same way
When two or more exceptions deserve the same recovery, you don't have to repeat yourself. Group them in a tuple:
try:
return int(data[key])
except (KeyError, ValueError):
return None
That single block matches if data[key] raises KeyError or if
int(...) raises ValueError. The parentheses are required — without
them you'd be writing something different and confusing (more on that
in a later chapter). Read it as: "if any one of these classes flies
out, do this."
When to reach for this shape: the recovery is identical, and a reader
shouldn't need to know which specific exception fired. "Either way, we
fall back to None."
The as form: bind the exception to a variable
When you do want to know what fired — to log it, to inspect it, to
re-raise it — bind it to a name with as:
except (KeyError, ValueError) as err:
print(f"failed: {type(err).__name__}")
Now inside the block, err is the actual exception instance. You can
read its attributes (err.args, err.__class__.__name__), pass it to a
logger, or include its message in the response you return. This is the
form professional Python code uses for anything more interesting than
"return a fallback and move on."
The editor on the right combines both shapes:
def parse_record(data, key):
try:
return int(data[key])
except (KeyError, ValueError) as err:
print(f"could not parse {key!r}: {type(err).__name__}")
return None
Three calls, three outcomes:
parse_record({"age": "42"}, "age")— happy path. Returns42.parse_record({"age": "n/a"}, "age")—int("n/a")raisesValueError. The except logscould not parse 'age': ValueErrorand returnsNone.parse_record({"age": "42"}, "name")—data["name"]raisesKeyError. Same except runs, buttype(err).__name__reflects which class actually fired. Logscould not parse 'name': KeyError.
Same handler, same fallback, but the log line tells you which failure mode you hit. That's the win: one block, one fallback, full visibility.
Where AI specifically gets this wrong
Two patterns to flag.
One: bundling exceptions that need different handling. Cursor
sometimes writes except (KeyError, ValueError, TypeError) as e: pass
because catching more feels safer. It isn't. TypeError usually means
"the code itself is wrong" and should crash. Only group exceptions in a
tuple when the recovery is genuinely the same.
Two: using as e and then ignoring e. If the variable is bound
and never read, you've added noise. Either log it, return it, or drop
the as clause entirely. AI ships dead as e bindings constantly —
they're a smell that the prompt asked for "good error handling" without
saying what to do with the error.
Run the editor and watch the same except block print three different
class names depending on which line in the try actually failed.