









Python Custom Exceptions - Define and Use Your Own Errors
Introduction to Python Custom Exceptions
In Python, not every error fits into the neat boxes of ValueError
, IndexError
, or TypeError
. Sometimes, your program has its own rules — and when those are broken, you want your own error messages. That’s where custom exceptions come in.
Defining your own exception class helps you raise meaningful, specific errors that are easier to debug and maintain. This tutorial walks you through everything you need to know — from syntax to output — about creating and using custom exceptions in Python.
Why Use Custom Exceptions?
- Readability: Custom exception names clearly explain what went wrong.
- Modularity: Group specific behaviors under logical exception types.
- Control: Handle different error types more granularly during exception handling.
How to Define a Custom Exception in Python
To define a custom exception, we subclass the built-in Exception
class. Here's the basic syntax:
class MyCustomError(Exception):
pass
This creates a new exception type called MyCustomError
. Now, you can raise it using the raise
keyword.
raise MyCustomError("Something went wrong in your custom logic!")
Basic Example: Custom Validation Exception
Let’s say you’re writing a function that only accepts even numbers. If the input is odd, you want to raise your own error.
class NotEvenNumberError(Exception):
def __init__(self, number):
super().__init__(f"The number {number} is not even.")
def check_even(n):
if n % 2 != 0:
raise NotEvenNumberError(n)
return f"{n} is even."
# Test
print(check_even(10))
print(check_even(7))
10 is even.
Traceback (most recent call last):
...
NotEvenNumberError: The number 7 is not even.
How to Customize Exception Messages
You can go beyond basic text by passing arguments and overriding the __str__
method:
class LowBalanceError(Exception):
def __init__(self, balance, amount):
self.balance = balance
self.amount = amount
def __str__(self):
return f"Transaction failed: Balance (${self.balance}) is less than amount (${self.amount})."
def withdraw(balance, amount):
if amount > balance:
raise LowBalanceError(balance, amount)
return balance - amount
# Test
print(withdraw(100, 50))
print(withdraw(100, 150))
50
Traceback (most recent call last):
...
LowBalanceError: Transaction failed: Balance ($100) is less than amount ($150).
Important Guidelines When Creating Custom Exceptions
- Always inherit from
Exception
or a subclass of it — never fromBaseException
unless you have a rare system-level reason. - Use descriptive names like
UserNotFoundError
orConfigFileMissingError
. - Consider using docstrings in your custom exception classes if they’ll be reused in larger applications.
Organizing Multiple Custom Exceptions
If you're building a large application, group related exceptions under a base class:
class AppError(Exception):
"""Base class for all application-specific errors."""
pass
class ConfigError(AppError):
pass
class DatabaseError(AppError):
pass
When to Raise Custom Exceptions
Use them when your application rules are broken. Examples:
- File not found in a specific directory even though the path exists
- Invalid user input beyond what's handled by
ValueError
- When you need user-specific error feedback
How to Verify Custom Exceptions Work
You can test them using try/except
blocks:
class LowBalanceError(Exception):
def __init__(self, balance, amount):
self.balance = balance
self.amount = amount
def __str__(self):
return f"Transaction failed: Balance (${self.balance}) is less than amount (${self.amount})."
def withdraw(balance, amount):
if amount > balance:
raise LowBalanceError(balance, amount)
return balance - amount
try:
withdraw(20, 50)
except LowBalanceError as e:
print("Caught:", e)
Caught: Transaction failed: Balance ($20) is less than amount ($50).
Conclusion
Custom exceptions give your code clarity and precision. Whether you’re building libraries, APIs, or full-stack apps, defining your own exceptions helps express intent and manage errors gracefully.