Define custom exceptions in Python

Create application exceptions from Exception or a specific built-in type, preserve normal exception behavior, and expose structured error details.

Best practices
Python programming and application logging

Define a class derived from Exception, or from a more specific built-in exception when its meaning fits. Use an Error suffix for ordinary application errors and keep the exception hierarchy understandable to callers.

Start with a small exception class

class AppError(Exception):
    pass

try:
    raise AppError("Configuration could not be loaded")
except AppError as exc:
    print(str(exc))

A custom initializer is unnecessary when all you need is an error message. The inherited initializer already stores arguments and provides a string representation.

When you need structured details, add attributes while preserving the base initializer:

class InvalidConfigError(ValueError):
    def __init__(self, message, *, field):
        super().__init__(message)
        self.field = field

error = InvalidConfigError("Timeout must be positive", field="timeout")
print(str(error))   # Timeout must be positive
print(error.args)   # ('Timeout must be positive',)
print(error.field)  # timeout

Callers can catch InvalidConfigError precisely or catch it through ValueError. Choose that base because the failure is an invalid value, not merely to reuse a class name.

Define a project-level hierarchy

class AppError(Exception):
    pass

class DatabaseError(AppError):
    pass

class ConfigError(AppError):
    pass

except AppError handles this project's error family, while specific handlers can distinguish database and configuration failures. Put the more specific handlers first. Catching a broad family does not remove the need to log, report, or recover from the failure appropriately.

FAQ

Should I inherit from BaseException? Ordinary application errors should derive from Exception. BaseException also covers control-flow exceptions such as SystemExit and KeyboardInterrupt; a direct subclass would not be caught by except Exception.

Do I need __str__? Usually not. Pass the message to the base initializer. Override string formatting only when there is a concrete display requirement.

Where should extra information go? Store stable fields such as an error code or field name as attributes, rather than requiring callers to parse the message. Avoid embedding secrets in messages or payloads likely to be logged.

What if the custom error wraps another exception? Use raise AppError("Loading failed") from exc inside the handler to retain the causal relationship for debugging.

References

Get a tailored plan

Contact us

Join the community

Scan with WeChat
to join the community

Try Guance

Start online and pay only for what you use.

Get started

Choose a Guance plan

Code hosting