try / except / else / finally

Run code that might fail, handle specific exceptions, and optionally run cleanup code whether it failed or not.

                try:
    ...
except SpecificError:
    ...
else:
    ...
finally:
    ...

              

Purpose

A try block runs code that might raise an exception. except catches and handles specific exception types; else runs only if the try block raised nothing at all; finally always runs, whether an exception happened or not, whether it was caught or not.

Example

def parse_positive_int(raw: str) -> int:
    try:
        value = int(raw)
    except ValueError:
        raise ValueError(f"{raw!r} is not a valid integer") from None
    else:
        if value <= 0:
            raise ValueError(f"{value} is not positive")
        return value

Behavior notes

  • except SpecificError: catches only that type (and its subclasses) — code that isn’t part of the try block, and errors of a different type, both propagate normally.
  • else runs only when the try block completed without raising — it exists specifically so the “what to do if it worked” logic doesn’t accidentally sit inside the try block, where it would also be covered by the except clause if it happened to raise the same exception type. Above, the validity check runs only once int(raw) has already succeeded.
  • finally always runs — even if the except block itself re-raises, even if a return fires inside the try or except block. It’s the right place for cleanup that must happen regardless of outcome (closing a file, releasing a lock).
  • raise NewError(...) from None (used above) suppresses Python’s default “during handling of the above exception, another exception occurred” chaining note — appropriate here because the original ValueError from int() isn’t useful context for the caller; the new message already says everything they need.

Common mistakes

  • except: with no exception type (a “bare except”) catches everything, including KeyboardInterrupt and typos that raised NameError — silently swallowing bugs that should have crashed loudly. Catch the specific exception type the code can actually recover from.
  • Putting code that shouldn’t be covered by except inside the try block anyway — anything that can also raise the same exception type for an unrelated reason will be caught and misdiagnosed as the original failure.
  • Forgetting that finally runs even after a return inside try/except — a return inside finally itself silently discards whatever the try/except was about to return, which is almost never intended.

Implementation notes

Exception-handling control flow — which clause runs when, and the guarantee that finally always runs — is defined by the language itself, identical across conforming implementations.

Taught in these lessons

Related reference