Error Handling
Errors should bubble up. Handle them at the highest layer, not scattered throughout the codebase.
┌─────────────────────────────────────────┐
│ TOP LAYER │ ← Handle errors HERE
│ (entry point) │
│ │
│ try: │
│ process_request(req) │
│ except Exception as e: │
│ log_error(e) │
│ return error_response(e) │
│ │
└───────────────┬─────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ MIDDLE LAYERS │ ← Let errors propagate
│ │
│ # No try/except here │
│ user = get_user(id) │
│ result = process(user) │
│ return result │
│ │
└───────────────┬─────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ LEAF LAYERS │ ← Raise meaningful errors
│ │
│ if not row: raise NotFoundError() │
│ │
└─────────────────────────────────────────┘
The Three Layers
- Top (entry points) — HTTP handlers, CLI commands, event handlers. Catch, log, and translate to user-facing output.
- Middle (orchestration) — Let errors propagate. No catching unless you can meaningfully recover and subsequent code can still run.
- Leaf (data access, APIs) — Raise meaningful, specific errors. The error name should tell the story.
Catch-log-rethrow is noise. Swallowing errors hides problems. Warnings are unresolved errors — treat them that way.
When Catching is Appropriate
# ✓ Subsequent code can still run - partial failure is acceptable
def validate(item):
try: return check_rule(item)
except Exception as e: return {"item": item, "error": str(e)}
def validate_all(items):
return [validate(item) for item in items]
# ✓ Resource cleanup that must happen
def with_connection(fn):
conn = connect()
try:
return fn(conn)
finally:
conn.close()
Anti-patterns
# ✗ Catch-log-rethrow adds noise, not value
try:
do_thing()
except Exception as e:
print(f"Error in do_thing: {e}")
raise
# ✗ Swallowing errors hides problems
try:
do_thing()
except Exception:
pass
# ✗ Defensive catching in middle layers
def get_user(id):
try:
return db.find_user(id)
except Exception:
return None # Now caller can't distinguish "not found" from "database down"