Custom Exceptions (Domain Errors)
On this page
Why Custom Exceptions
Custom exceptions let callers handle failures by category (validation vs dependency vs state) and keep error policies consistent.
Define a Small Hierarchy
class AppError(Exception):
pass
class ValidationError(AppError):
pass
class DependencyError(AppError):
pass
Use at Boundaries
def parse_user_id(value: str) -> int:
try:
user_id = int(value)
except ValueError as e:
raise ValidationError("user_id must be an int") from e
if user_id <= 0:
raise ValidationError("user_id must be > 0")
return user_id
Operational Checklist
- Keep the hierarchy shallow and meaningful.
- Raise domain errors at boundaries; do not leak low-level exceptions everywhere.
- Attach actionable messages (what failed and why).
Failure Modes
- Exception sprawl: too many types create confusion.
- Leaky abstractions: low-level errors bubble up without policy.