Error handling with try/except

The basic shape — and how it compares

Most languages you’ve likely used have some form of this same pattern — Java/C# call it try/catch, JS is nearly identical to Python’s spelling:

try {
    result = 10 / 0;
} catch (ArithmeticException e) {
    result = -1;
}

Python’s version:

Try it — edit and run

Same idea, different keyword (except instead of catch), and Python names the exception type directly after except rather than in parentheses.

Seeing the crash first

Without a try, an error stops the program entirely — you’ve already seen this in Concept 1’s execution-model example. Here’s the same idea, specifically for division:

Try it — edit and run

Wrapping the risky line in try/except lets you handle it instead of crashing:

Try it — edit and run

The try block runs normally. If the specific error named in except occurs anywhere inside it, execution jumps straight to that except block — everything else remaining in try is skipped.

Catching specific errors vs. catching everything

You can name the exact error type you expect (recommended), or catch everything with a bare except::

Try it — edit and run

This runs — but it’s hiding the actual problem: divide(10, "two") fails because you can’t divide by a string (TypeError), not because of division by zero. A bare except: swallows that distinction, which makes bugs harder to find later. Naming the specific type keeps you honest about what you’re actually expecting to go wrong:

Try it — edit and run

Multiple except clauses can follow one try, each catching a different error type.

Order matters with multiple except clauses

Python checks except clauses top to bottom and uses the first one that matches — same as elif. This matters when exception types overlap: ZeroDivisionError is actually a subclass of ArithmeticError, so if you catch the broader type first, the more specific one never gets a chance to run.

Try it — edit and run

The second except isn’t wrong, it’s just unreachable for this case. The fix is ordering specific exceptions before general ones:

Try it — edit and run

finally — runs no matter what

A finally block runs whether the try succeeded, failed, or was caught — useful for cleanup that must happen regardless (closing a file, releasing a connection):

Try it — edit and run

Notice "divide attempt finished" prints before the return value in each case: finally runs after the try/except resolves, but before the function actually returns to the caller.

Reading a traceback

You’ve seen a few tracebacks already in this lesson without a formal breakdown — here’s what’s actually in one:

Traceback (most recent call last):
  File "script.py", line 4, in <module>
    print(divide(10, 0))
  File "script.py", line 2, in divide
    return a / b
ZeroDivisionError: division by zero

Read it as a call stack, top to bottom: line 4 called divide, which failed on line 2. The last line is the one that matters most day-to-day — it names the exception type and the specific message. When something breaks, start there, then use the lines above it to trace where the call came from if the error itself isn’t enough context.

Check your understanding
1/6

What's the main syntax difference between Python's `try`/`except` and Java/C#'s `try`/`catch`?

Exercise · Graded

If `message` isn't a string, checking `"calculate" in message` raises a `TypeError`. Wrap the function so that case returns `"invalid input"` instead of crashing, while valid strings still route normally through the existing if/elif chain. (Composes Concept 2's control flow with Concept 3's try/except.)