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.

Errors & contextPython 3 (all)Live demo
try / except
try:
    risky()
except ValueError as e:
    handle(e)
several types
try:
    risky()
except (KeyError, IndexError):
    handle()
all four clauses
try:
    risky()
except OSError:
    on_error()
else:
    on_success()
finally:
    cleanup()
except* (3.11+)
try:
    run_tasks()
except* ValueError as eg:
    handle(eg.exceptions)
Use for
except SomeError as e: for errors you can handle; finally: for cleanup that must always run
Result
a statement — no value; the as name is deleted when the except block ends
Pairs with
raise, with, else, as, ExceptionGroup
Watch out
catch specific types; a return in finally silently discards the exception

Demo

Live evaluation
Which blocks run, and in which order? Divide by zero to take the except path.
Try:
Inputs
dinttry 0
Code
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
Result
['try', 'try finished', 'else', 'finally']

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

NameTypeRequiredDescription
try bodyblockyesThe code being protected. Keep it small — only the lines that can raise what you catch.
exceptexpression (class/tuple)noMatches 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 namenamenoBound to the exception object inside the except block, then deleted when the block ends.
elseblocknoRuns only if the try body finished without raising. Its own errors are not caught by the except clauses above.
finallyblocknoRuns last, on every way out: normal end, exception, return, break or continue.

Common patterns

Handle one expected error
Catch the narrowest type, at the one line that can raise it.
try:
    port = int(text)
except ValueError:
    port = 8080
else for the success path
Code in else is not protected, so a bug there is not mistaken for the error you meant to catch.
try:
    f = open(path)
except FileNotFoundError:
    data = ''
else:
    with f:
        data = f.read()
Log and re-raise
Bare raise inside except re-raises the same exception with its traceback.
try:
    process(job)
except Exception:
    log.exception('job failed')
    raise
Always release a resource
finally runs even on return or an exception; with is the shorter form when the object is a context manager.
lock.acquire()
try:
    update(shared)
finally:
    lock.release()

Examples

1. Catch and inspect the error
try: int('abc') except ValueError as e: print('bad number:', e)
Returns
bad number: invalid literal for int() with base 10: 'abc'
2. One clause, several types
for raw in ['7', 'x', None]: try: print(int(raw)) except (ValueError, TypeError) as e: print(type(e).__name__)
Returns
7 ValueError TypeError
3. else and finally
def run(x): try: r = 10 / x except ZeroDivisionError: return 'except' else: return f'else {r}' finally: print('finally') print(run(2)) print(run(0))
Returns
finally else 5.0 finally except
4. finally runs on break too
for i in range(3): try: if i == 1: break finally: print('cleanup', i)
Returns
cleanup 0 cleanup 1
5. First matching clause wins
try: {}['k'] except LookupError: print('LookupError clause') except KeyError: print('never reached')
Returns
LookupError clause
6. Bare except also catches Ctrl+C and exit
for exc in (ValueError, KeyboardInterrupt, SystemExit): try: raise exc except Exception: print(exc.__name__, '-> except Exception') except: print(exc.__name__, '-> bare except')
Returns
ValueError -> except Exception KeyboardInterrupt -> bare except SystemExit -> bare except
7. except* splits an ExceptionGroup
try: raise ExceptionGroup('batch', [ValueError('a'), TypeError('b'), ValueError('c')]) except* ValueError as eg: print('values:', eg.exceptions) except* TypeError as eg: print('types:', eg.exceptions)
Returns
values: (ValueError('a'), ValueError('c')) types: (TypeError('b'),)

Pitfalls

1. return in finally swallows the exception
A return (or break / continue) in finally discards the exception in flight — the caller never hears about it. Python 3.14 adds a SyntaxWarning for this; 3.13 says nothing.
return in finally
def save():
    try:
        raise OSError('disk full')
    finally:
        return 'saved'
save()
'saved'
cleanup only
def save():
    try:
        raise OSError('disk full')
    finally:
        print('cleanup')
save()
cleanup OSError: disk full
2. Bare except hides real bugs
except: (and except Exception:) turn every mistake into the fallback value. Name the error you expect; anything else should crash loudly.
catch everything
def parse(s):
    try:
        return int(s)
    except:
        return 0
(parse('12a'), parse(None))
(0, 0)
catch ValueError
def parse(s):
    try:
        return int(s)
    except ValueError:
        return 0
(parse('12a'), parse(None))
TypeError: int() argument must be a string, a bytes-like object or a real number, not 'NoneType'
3. Using the as name after the except block
Python deletes the name at the end of the except block (it would otherwise keep the whole stack frame alive). Copy it to another name if you need it later.
read err afterwards
try:
    int('x')
except ValueError as err:
    pass
print(err)
NameError: name 'err' is not defined
keep a copy
error = None
try:
    int('x')
except ValueError as err:
    error = err
error
ValueError("invalid literal for int() with base 10: 'x'")

When to use

Use it
  • 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)
Reach for something else
  • 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

CPython impl
try costs almost nothing when no exception is raised (3.11+ "zero-cost" exceptions: the handler table is only consulted when something is raised)
Matching
except X matches if isinstance(exc, X); order clauses from most specific to most general — a base class listed first shadows its subclasses
as
The as name is unbound when the except block ends (as if del name ran in a hidden finally). The same keyword binds names in with and import
except*
Added in 3.11 for ExceptionGroup. A try uses either except or except*, never both, and except*: without a type is a SyntaxError

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.

History

3.8
continue became legal inside a finally clause.
3.11
except* for ExceptionGroup (PEP 654).
3.14
Parentheses around several exception types may be dropped when there is no as clause (PEP 758); return, break or continue in finally now emits a SyntaxWarning (PEP 765).