reference
5 min read
·
lesson 15 of 16 in Reference
Python Built-in Exceptions Cheat Sheet
1 · The lesson
readQuick 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
| Exception | Raised when | Parent | Typical fix |
|---|---|---|---|
ValueError | Right type, bad value (int("x")) | Exception | Validate input; try/except around parse |
TypeError | Wrong type (len(5), "a"+1) | Exception | Convert (str(n)); fix call signature |
KeyError | Missing dict key | LookupError | d.get(k, default) or if k in d |
IndexError | Index out of range | LookupError | Check len(seq); use try or slicing |
AttributeError | Missing attribute (None.foo) | Exception | Guard None; check spelling; getattr(obj, "x", default) |
NameError | Reference to undefined name | Exception | Define it; check import |
UnboundLocalError | Local var used before assignment | NameError | Use global/nonlocal or initialise above |
ZeroDivisionError | Division by zero | ArithmeticError | Guard divisor |
OverflowError | Float result too large | ArithmeticError | Use math.log form; ints don't overflow in Python |
ImportError | Import failed at any stage | Exception | Check install: pip install ... |
ModuleNotFoundError | Module name unknown | ImportError | Install or fix the path |
FileNotFoundError | Path doesn't exist | OSError | Check Path.exists() first; create parent dir |
FileExistsError | Mode x and file exists | OSError | Choose w or check |
PermissionError | Read-only / locked path | OSError | Close other handle; check ACL |
IsADirectoryError | Opened a directory like a file | OSError | Pass the file path, not its parent |
NotADirectoryError | Listed a file as a directory | OSError | Pass directory path |
TimeoutError | System call timed out | OSError | Increase timeout; retry |
ConnectionError | Generic connection failure | OSError | Retry; check network |
BrokenPipeError | Wrote to a closed pipe | ConnectionError | Handle SIGPIPE; check the consumer |
UnicodeDecodeError | Bytes -> str failed | UnicodeError | Pass encoding="utf-8"; errors="replace" |
UnicodeEncodeError | str -> bytes failed | UnicodeError | Use UTF-8; avoid str.encode("ascii") |
RuntimeError | Generic runtime issue | Exception | Read message; specific subclass exists for many cases |
RecursionError | Max recursion depth exceeded | RuntimeError | Convert to loop; raise limit with sys.setrecursionlimit |
NotImplementedError | Abstract method not overridden | RuntimeError | Implement the method |
AssertionError | assert failed | Exception | Fix the invariant; do not use assert for prod input checks |
StopIteration | Iterator exhausted (used by next()) | Exception | Provide default: next(it, default) |
StopAsyncIteration | Async iterator exhausted | Exception | Same idea, async version |
KeyboardInterrupt | User pressed Ctrl+C | BaseException | Catch only if you'll re-raise after cleanup |
SystemExit | sys.exit() called | BaseException | Don't catch unless you really mean to |
MemoryError | Out of memory | Exception | Stream input; reduce batch size |
SyntaxError | Parser failed | Exception | Fix 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-pattern | Why it's bad | Better |
|---|---|---|
except: (bare except) | Catches SystemExit, KeyboardInterrupt too | except Exception: |
except Exception: pass | Silently hides bugs | Log it, narrow it, or let it raise |
| Catching too high | Hides where the error came from | Catch at the smallest meaningful scope |
Losing traceback with raise NewError(str(e)) | Throws away cause | raise NewError(...) from e |
assert for production input checks | Stripped under python -O | if not cond: raise ValueError(...) |
Catching Exception then print | Logs without traceback | log.exception(...) |
Using == to compare exception types | Doesn't match subclasses | isinstance(e, OSError) |
Catching KeyboardInterrupt without re-raising | Breaks Ctrl+C | Re-raise after cleanup |
See Also
- Exceptions (tutorial)
- Exceptions basics
- File I/O cheat for
OSErrorfamily - All
error-*pages for deep-dives on individual exceptions
Practice this
on practicepython.inShort exercises that run in your browser and tell you what your code actually did, not just whether a test passed.