Question
How to Declare Custom Exceptions in Modern Python
Question
How can I declare custom exception classes in modern Python?
I want to follow the same conventions used by built-in exceptions so that, for example, any extra message I include is displayed correctly when the exception is caught and printed.
By modern Python, I mean code that works in Python 2.5 while also following the style that is correct for Python 2.6 and Python 3.x.
By custom exception, I mean an exception type that can store extra information about the error, such as a message string and possibly another arbitrary object related to the failure.
I ran into this deprecation warning in Python 2.6.2:
class MyError(Exception):
def __init__(self, message):
self.message = message
MyError("foo")
This produces:
DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
It seems that BaseException gives special meaning to an attribute named message. From PEP 352, I understand that this attribute had special behavior in Python 2.5 and was later deprecated, so it appears that using message as a custom attribute name is no longer a good idea.
I also know that Exception has an args attribute, but I have never been sure how it is meant to be used, or whether it is still the recommended approach in Python 3.
What is the correct way to define custom exceptions so they behave like standard exceptions and can also store extra data?
Short Answer
By the end of this page, you will understand how to create custom exceptions in Python by subclassing Exception, passing error text to the base class correctly, and optionally storing extra context safely. You will also learn how args, __str__, and custom attributes work, and how to write exception classes that behave like built-in exceptions.
Concept
In Python, custom exceptions are usually created by subclassing Exception or one of its subclasses.
The key idea is this:
- An exception is just a class
- Raising an exception means creating an instance of that class
- Built-in exception behavior depends partly on the base class storing constructor values in
args
A correct custom exception should usually do two things:
- Inherit from
Exception - Call
Exception.__init__(...)with the message or values you want the exception to display
For example:
class MyError(Exception):
pass
This is already a valid custom exception.
You can raise it like this:
raise MyError("Something went wrong")
Python stores the supplied values in exception.args. When the exception is printed, Python usually uses those values to build the string representation.
That is why calling the parent constructor matters.
Why message caused a warning
Mental Model
Think of an exception like a labeled emergency report.
- The exception class is the category of the report, like
ValidationErrororFileFormatError - The message is the human-readable description of what went wrong
- The extra attributes are attached evidence, like a filename, record ID, or bad value
Built-in exceptions already know how to print the report summary because they store the main message in a standard place: args.
So when you create your own exception, the safest pattern is:
- put the printable message into the base
Exception - put any extra information into your own attributes
That way, Python and other tools can still print the error naturally, while your code can access more detailed context when needed.
Syntax and Examples
Basic custom exception
class MyError(Exception):
pass
raise MyError("Something went wrong")
This works because Exception already knows how to store the message in args.
Custom exception with extra data
class MyError(Exception):
def __init__(self, message, payload=None):
super(MyError, self).__init__(message)
self.payload = payload
Usage:
try:
raise MyError("Invalid input", payload={"field": "age", "value": -1})
except MyError as e:
print(str(e))
print(e.payload)
Step by Step Execution
Consider this example:
class ValidationError(Exception):
def __init__(self, field, value):
message = "Invalid value for %s: %r" % (field, value)
super(ValidationError, self).__init__(message)
self.field = field
self.value = value
try:
raise ValidationError("age", -1)
except ValidationError as e:
print("message:", str(e))
print("args:", e.args)
print("field:", e.field)
print("value:", e.value)
What happens step by step
-
Python defines the class
ValidationError. -
raise ValidationError("age", -1)creates a new exception object. -
Inside
__init__, the code builds this message:
Real World Use Cases
Custom exceptions are useful whenever your program needs to express specific failure types clearly.
Input validation
class ValidationError(Exception):
pass
Used when a user enters invalid data in a form or API request.
File and configuration loading
class ConfigError(Exception):
pass
Used when a config file is missing required keys or contains invalid values.
API clients
class ApiError(Exception):
def __init__(self, message, status_code=None):
super(ApiError, self).__init__(message)
self.status_code = status_code
Useful when you want to store the HTTP status code along with the message.
Data processing pipelines
class RecordFormatError():
():
(RecordFormatError, ).__init__(message)
.record = record
Real Codebase Usage
In real projects, developers usually organize exceptions around domains and use them to support clear error handling.
Common pattern: base application exception
class AppError(Exception):
pass
class ValidationError(AppError):
pass
class DatabaseError(AppError):
pass
This lets you catch all application-specific problems with one base class:
try:
do_work()
except AppError as e:
print("Application error:", e)
Pattern: add structured context
class ApiError(Exception):
def __init__(self, message, status_code, response_body=None):
super(ApiError, self).__init__(message)
self.status_code = status_code
self.response_body = response_body
Common Mistakes
1. Forgetting to call the base class constructor
Broken:
class MyError(Exception):
def __init__(self, message):
self.text = message
Problem:
str(e)may not behave as expectedargswill not contain the message
Better:
class MyError(Exception):
def __init__(self, message):
super(MyError, self).__init__(message)
self.text = message
2. Using message as a custom attribute in old Python versions
Problematic in Python 2.6:
class MyError(Exception):
def __init__():
.message = message
Comparisons
Custom exception patterns compared
| Approach | Example | Good for | Downsides |
|---|---|---|---|
| Simple subclass | class MyError(Exception): pass | Distinct error type with no extra data | Cannot store named context unless added later |
| Subclass with message passed to base class | super(...).__init__(message) | Standard printable behavior | Slightly more code |
| Subclass with custom attributes | self.code = code | Rich debugging and structured handling | Must design attributes clearly |
| Only store custom attributes, do not call base class | self.text = message | Almost never recommended | Loses standard exception behavior |
Cheat Sheet
Quick pattern
class MyError(Exception):
def __init__(self, message, data=None):
super(MyError, self).__init__(message)
self.data = data
Raise it
raise MyError("Something went wrong", data={"id": 123})
Catch it
try:
do_work()
except MyError as e:
print(e) # human-readable message
print(e.args) # standard exception arguments
print(e.data) # custom extra data
Rules to remember
- Inherit from
Exception - Pass the printable message to the base class
- Store extra context in your own attributes
- Avoid using
self.messagein old Python compatibility code - Use simple subclasses when extra data is not needed
FAQ
Should custom exceptions inherit from Exception or BaseException?
Use Exception. BaseException is meant for special built-in exceptions such as SystemExit and KeyboardInterrupt.
How do I include a message in a custom Python exception?
Pass the message to the parent constructor:
super(MyError, self).__init__(message)
This stores the text in args so it prints naturally.
Can a custom exception store extra data?
Yes. Add your own attributes such as code, filename, field, or payload.
Should I use self.message in a custom exception?
Avoid it, especially in Python 2.6 compatibility code, because .message had deprecated special behavior.
What is args on an exception?
Mini Project
Description
Build a small input validation module that checks user registration data and raises custom exceptions when values are invalid. This demonstrates how to define readable exception classes, attach extra context, and handle specific failures cleanly.
Goal
Create a validator that raises custom exceptions with both a human-readable message and structured data about the invalid field.
Requirements
- Create a custom exception class for validation errors
- Store a printable message in the base exception
- Store the invalid field name and value as custom attributes
- Write a function that validates a username and age
- Raise the custom exception when validation fails
- Catch the exception and print both the message and extra data
Keep learning
Related questions
@staticmethod vs @classmethod in Python Explained
Learn the difference between @staticmethod and @classmethod in Python with clear examples, use cases, mistakes, and a mini project.
Call a Function by Name in a Python Module
Learn how to call a function by name in a Python module using strings, getattr, and safe patterns for dynamic function dispatch.
Catch Multiple Exceptions in One except Block in Python
Learn how to catch multiple exceptions in one Python except block using tuples, with examples, mistakes, and real-world usage.