Course Progress52%
🍎 Python Error Handling Topic 52 / 100
⏳ 7 min read

Custom Exceptions

Create your own exception types so errors in your code are specific, descriptive, and easy to catch separately from built-in Python errors.

"Built-in exceptions like ValueError are generic. A custom exception like InsufficientFundsError tells you exactly what went wrong, just by reading its name."

— ShurAI

Why Create Custom Exceptions?

Python has many built-in exceptions, but they are generic. When you write a library or application, custom exceptions make errors self-documenting and easy to catch specifically:

🔴 Generic — what went wrong?
raise ValueError("bad")

except ValueError:
    # Was it age? name?
    # balance? username?
    # We have no idea.
🟢 Specific — instantly clear
raise InsufficientFundsError()

except InsufficientFundsError:
    # Unambiguous.
    # Show "not enough balance"
    # message to the user.

Creating a Custom Exception

A custom exception is just a class that inherits from Exception. The simplest version needs only one line of body:

python — the minimal pattern
class InsufficientFundsError(Exception):
    pass   # inherits everything from Exception

# Use it exactly like any built-in exception
raise InsufficientFundsError("Not enough balance for this transaction")
# InsufficientFundsError: Not enough balance for this transaction

Adding a Custom Message

Override __init__ to embed extra context automatically into the error message:

python
class InsufficientFundsError(Exception):
    def __init__(self, balance, amount):
        self.balance = balance
        self.amount  = amount
        super().__init__(
            f"Cannot withdraw {amount}. Balance is only {balance}."
        )

def withdraw(balance, amount):
    if amount > balance:
        raise InsufficientFundsError(balance, amount)
    return balance - amount

try:
    withdraw(500, 800)
except InsufficientFundsError as e:
    print(f"Error: {e}")
    print(f"You have {e.balance}, need {e.amount}")
output
Error: Cannot withdraw 800. Balance is only 500.
You have 500, need 800

Exception Hierarchy — Group Related Errors

Create a base exception for your app, then specific sub-exceptions. You can catch them individually or all at once using the base:

python
# Base exception for the whole app
class ShopError(Exception):
    pass

# Specific exceptions inherit from ShopError
class OutOfStockError(ShopError):
    pass

class InvalidCouponError(ShopError):
    pass

class PaymentFailedError(ShopError):
    pass

# Catch a specific one
try:
    raise OutOfStockError("Rice is out of stock")
except OutOfStockError as e:
    print(f"Stock issue: {e}")

# OR catch any shop error at once
try:
    raise InvalidCouponError("Coupon SAVE50 has expired")
except ShopError as e:
    print(f"Shop error: {e}")
Exception hierarchy for the shop app
Exception  (Python built-in)
↓ inherits
ShopError  (your base)
↓ ↓ ↓
OutOfStockError
InvalidCouponError
PaymentFailedError

Real Example — User Registration

python
class RegistrationError(Exception):
    pass

class UsernameTakenError(RegistrationError):
    def __init__(self, username):
        super().__init__(f"Username '{username}' is already taken")

class WeakPasswordError(RegistrationError):
    def __init__(self):
        super().__init__("Password must be at least 8 characters")

existing_users = ["riya", "arjun"]

def register(username, password):
    if username.lower() in existing_users:
        raise UsernameTakenError(username)
    if len(password) < 8:
        raise WeakPasswordError()
    print(f"✓ Registered: {username}")

tests = [("Sneha", "secure123"), ("riya", "pass"), ("Dev", "123")]
for user, pwd in tests:
    try:
        register(user, pwd)
    except UsernameTakenError as e:
        print(f"✗ {e}")
    except WeakPasswordError as e:
        print(f"✗ {e}")
output
✓ Registered: Sneha
✗ Username 'riya' is already taken
✗ Password must be at least 8 characters

"One custom exception class costs you two lines. It buys you crystal-clear error messages, specific catching, and code that reads like natural language."

— ShurAI

🧠 Quiz — Q1

What do you write to create the simplest possible custom exception called GameOverError?

🧠 Quiz — Q2

Why is InsufficientFundsError better than ValueError for a bank app?

🧠 Quiz — Q3

If OutOfStockError inherits from ShopError, which except blocks can catch it?

🧠 Quiz — Q4

What does super().__init__("message") do inside a custom exception’s __init__?