try / except / finally
Python’s exception handler: except catches, else runs only when nothing was raised, finally runs no matter how the block is left.
try: risky() except ValueError as e: handle(e)
try: risky() except (KeyError, IndexError): handle()
try: risky() except OSError: on_error() else: on_success() finally: cleanup()
try: run_tasks() except* ValueError as eg: handle(eg.exceptions)
Demo
steps = [] try: steps.append('try') ratio = 10 / 4 steps.append('try finished') except ZeroDivisionError: steps.append('except') else: steps.append('else') finally: steps.append('finally') steps
In the order tab, the step after the failing line never runs: an exception jumps straight to the matching except, and else is skipped. finally is in the list either way. In the except (A, B) tab, a float index (type 1e21 or more) raises TypeError, which the clause does not name — so it escapes uncaught. In the finally tab, the return value is computed first, then finally runs, then the function actually returns.
Syntax slots
| Name | Type | Required | Description |
|---|---|---|---|
| try body | block | yes | The code being protected. Keep it small — only the lines that can raise what you catch. |
| except | expression (class/tuple) | no | Matches if the raised exception is an instance of the class (or of any class in the tuple). Clauses are tried top to bottom; the first match wins. A bare except: matches everything and must come last. |
| as name | name | no | Bound to the exception object inside the except block, then deleted when the block ends. |
| else | block | no | Runs only if the try body finished without raising. Its own errors are not caught by the except clauses above. |
| finally | block | no | Runs last, on every way out: normal end, exception, return, break or continue. |
Common patterns
try: port = int(text) except ValueError: port = 8080
try: f = open(path) except FileNotFoundError: data = '' else: with f: data = f.read()
try: process(job) except Exception: log.exception('job failed') raise
lock.acquire() try: update(shared) finally: lock.release()
Examples
Pitfalls
def save(): try: raise OSError('disk full') finally: return 'saved' save()
def save(): try: raise OSError('disk full') finally: print('cleanup') save()
def parse(s): try: return int(s) except: return 0 (parse('12a'), parse(None))
def parse(s): try: return int(s) except ValueError: return 0 (parse('12a'), parse(None))
try: int('x') except ValueError as err: pass print(err)
error = None try: int('x') except ValueError as err: error = err error
When to use
- An operation can fail for reasons outside your control (files, network, user input, parsing)
- You can do something useful about the failure: a default, a retry, a clearer error
- Cleanup must happen no matter what (finally)
- Opening files, locks, connections → with does the finally for you
- Checking a condition you can test cheaply and clearly → if (e.g. key in d, or d.get())
- Hiding errors you cannot handle → let them propagate
Notes
FAQ
Put the types in a tuple: except (ValueError, TypeError) as e:. Up to Python 3.13 the parentheses are required — except ValueError, TypeError: is a SyntaxError ("multiple exception types must be parenthesized"). 3.14 allows dropping them when there is no as clause.