PythonMastery
reference 5 min read · lesson 15 of 16 in Reference

Python Built-in Exceptions Cheat Sheet

1 · The lesson

read

Quick reference for Python's built-in exception hierarchy, the try shape, and patterns. For the full tutorial see Exceptions.

1. Hierarchy (Common Branches)

python
BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- BaseExceptionGroup           (3.11+)
 +-- Exception                    <-- catch this, not BaseException
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError
      |    +-- ZeroDivisionError
      |    +-- OverflowError
      |    +-- FloatingPointError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
      |    +-- ModuleNotFoundError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- OSError                  (aliases: IOError, EnvironmentError)
      |    +-- FileNotFoundError
      |    +-- FileExistsError
      |    +-- PermissionError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- InterruptedError
      |    +-- TimeoutError
      |    +-- ConnectionError
      |         +-- BrokenPipeError
      |         +-- ConnectionAbortedError
      |         +-- ConnectionRefusedError
      |         +-- ConnectionResetError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      |    +-- RecursionError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning                   (separate hierarchy)
      +-- ExceptionGroup            (3.11+)

2. Top Built-in Exceptions

ExceptionRaised whenParentTypical fix
ValueErrorRight type, bad value (int("x"))ExceptionValidate input; try/except around parse
TypeErrorWrong type (len(5), "a"+1)ExceptionConvert (str(n)); fix call signature
KeyErrorMissing dict keyLookupErrord.get(k, default) or if k in d
IndexErrorIndex out of rangeLookupErrorCheck len(seq); use try or slicing
AttributeErrorMissing attribute (None.foo)ExceptionGuard None; check spelling; getattr(obj, "x", default)
NameErrorReference to undefined nameExceptionDefine it; check import
UnboundLocalErrorLocal var used before assignmentNameErrorUse global/nonlocal or initialise above
ZeroDivisionErrorDivision by zeroArithmeticErrorGuard divisor
OverflowErrorFloat result too largeArithmeticErrorUse math.log form; ints don't overflow in Python
ImportErrorImport failed at any stageExceptionCheck install: pip install ...
ModuleNotFoundErrorModule name unknownImportErrorInstall or fix the path
FileNotFoundErrorPath doesn't existOSErrorCheck Path.exists() first; create parent dir
FileExistsErrorMode x and file existsOSErrorChoose w or check
PermissionErrorRead-only / locked pathOSErrorClose other handle; check ACL
IsADirectoryErrorOpened a directory like a fileOSErrorPass the file path, not its parent
NotADirectoryErrorListed a file as a directoryOSErrorPass directory path
TimeoutErrorSystem call timed outOSErrorIncrease timeout; retry
ConnectionErrorGeneric connection failureOSErrorRetry; check network
BrokenPipeErrorWrote to a closed pipeConnectionErrorHandle SIGPIPE; check the consumer
UnicodeDecodeErrorBytes -> str failedUnicodeErrorPass encoding="utf-8"; errors="replace"
UnicodeEncodeErrorstr -> bytes failedUnicodeErrorUse UTF-8; avoid str.encode("ascii")
RuntimeErrorGeneric runtime issueExceptionRead message; specific subclass exists for many cases
RecursionErrorMax recursion depth exceededRuntimeErrorConvert to loop; raise limit with sys.setrecursionlimit
NotImplementedErrorAbstract method not overriddenRuntimeErrorImplement the method
AssertionErrorassert failedExceptionFix the invariant; do not use assert for prod input checks
StopIterationIterator exhausted (used by next())ExceptionProvide default: next(it, default)
StopAsyncIterationAsync iterator exhaustedExceptionSame idea, async version
KeyboardInterruptUser pressed Ctrl+CBaseExceptionCatch only if you'll re-raise after cleanup
SystemExitsys.exit() calledBaseExceptionDon't catch unless you really mean to
MemoryErrorOut of memoryExceptionStream input; reduce batch size
SyntaxErrorParser failedExceptionFix the source code

3. The try Shape

python
try:
    risky()
except (ValueError, TypeError) as e:    # multiple in one tuple
    log.warning("bad input: %s", e)
except OSError:
    cleanup()
    raise                                # re-raise same exception
else:
    # ran only if no exception
    commit()
finally:
    # always runs (success, exception, return)
    close()
+ setup added so this can run · defines risky, commit, close, cleanup, log
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

def risky(*_a, **_kw):
    print('-> risky() called')
    return _AutoMock('risky()')
def commit(*_a, **_kw):
    print('-> commit() called')
    return _AutoMock('commit()')
def close(*_a, **_kw):
    print('-> close() called')
    return _AutoMock('close()')
def cleanup(*_a, **_kw):
    print('-> cleanup() called')
    return _AutoMock('cleanup()')
log = _AutoMock('log')

4. Re-raising

python
try:
    parse(s)
except ValueError:
    raise                                # same exception, same traceback

try:
    open(path)
except OSError as e:
    raise RuntimeError("could not load config") from e   # chains __cause__

try:
    f()
except KeyError:
    raise RuntimeError("missing field") from None        # hide the cause
+ setup added so this can run · defines parse, s, path, f
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

def parse(*_a, **_kw):
    print('-> parse() called')
    return _AutoMock('parse()')
s = _AutoMock('s')
path = _AutoMock('path')
def f(*_a, **_kw):
    print('-> f() called')
    return _AutoMock('f()')

5. Custom Exceptions

python
class AppError(Exception):
    """Base class for this app."""

class ConfigError(AppError):
    def __init__(self, key, *, msg=None):
        super().__init__(msg or f"Bad config key: {key}")
        self.key = key

raise ConfigError("api_token")

Subclass Exception, not BaseException.

6. ExceptionGroup and except* (3.11+)

python
try:
    raise ExceptionGroup("partial failure", [
        ValueError("bad row 3"),
        OSError("disk full"),
    ])
except* ValueError as eg:
    log.warning("data issues: %s", eg.exceptions)
except* OSError as eg:
    log.error("io issues: %s", eg.exceptions)
+ setup added so this can run · defines log
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

log = _AutoMock('log')

except* catches matching parts of a group, re-raises the rest.

Common Patterns

python
# Retry with backoff
import time

def retry(fn, attempts=3, exc=Exception, base=0.5):
    for i in range(attempts):
        try:
            return fn()
        except exc:
            if i == attempts - 1:
                raise
            time.sleep(base * (2 ** i))

# Swallow expected exception cleanly
from contextlib import suppress
with suppress(FileNotFoundError):
    Path("cache.tmp").unlink()

# Log and re-raise (don't lose the traceback)
try:
    work()
except Exception:
    log.exception("work failed")     # logs full traceback
    raise

# Cleanup with finally
try:
    f = open(path)
    use(f)
finally:
    f.close()       # better: use a `with` block

# Validate then act (EAFP — easier to ask forgiveness than permission)
try:
    val = d[key]
except KeyError:
    val = compute_default()

# Convert one exception to another (for API boundary)
try:
    int(s)
except ValueError as e:
    raise BadInput(f"expected integer, got {s!r}") from e
+ setup added so this can run · defines work, path, use, d, key, s, compute_default, BadInput, Path, log
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

def work(*_a, **_kw):
    print('-> work() called')
    return _AutoMock('work()')
path = _AutoMock('path')
def use(*_a, **_kw):
    print('-> use() called')
    return _AutoMock('use()')
d = _AutoMock('d')
key = _AutoMock('key')
s = _AutoMock('s')
def compute_default(*_a, **_kw):
    print('-> compute_default() called')
    return _AutoMock('compute_default()')
def BadInput(*_a, **_kw):
    print('-> BadInput() called')
    return _AutoMock('BadInput()')
def Path(*_a, **_kw):
    print('-> Path() called')
    return _AutoMock('Path()')
log = _AutoMock('log')

Common Errors / Anti-Patterns

Anti-patternWhy it's badBetter
except: (bare except)Catches SystemExit, KeyboardInterrupt tooexcept Exception:
except Exception: passSilently hides bugsLog it, narrow it, or let it raise
Catching too highHides where the error came fromCatch at the smallest meaningful scope
Losing traceback with raise NewError(str(e))Throws away causeraise NewError(...) from e
assert for production input checksStripped under python -Oif not cond: raise ValueError(...)
Catching Exception then printLogs without tracebacklog.exception(...)
Using == to compare exception typesDoesn't match subclassesisinstance(e, OSError)
Catching KeyboardInterrupt without re-raisingBreaks Ctrl+CRe-raise after cleanup

See Also

Practice this

on practicepython.in

Short exercises that run in your browser and tell you what your code actually did, not just whether a test passed.