Course Progress51%
🍎 Python Error Handling Topic 51 / 100
⏳ 7 min read

Raising Exceptions

You can throw exceptions yourself with raise — the right way to enforce rules and reject invalid data in your functions.

"raise is a full stop. It says: something is so wrong here that I refuse to continue. The caller must deal with this."

— ShurAI

The raise Statement

So far, exceptions happened automatically when something went wrong. With raise, you decide when to throw an error. This lets you enforce rules inside your functions:

python — basic raise
raise ValueError("Something went wrong")
# ValueError: Something went wrong

This is the pattern for every raise: the exception type, then a message in parentheses that explains what went wrong.

Validating Function Input

The most common use of raise is rejecting bad input before doing any real work:

python
def set_age(age):
    """Set a person's age. Must be 0-150."""
    if not isinstance(age, int):
        raise TypeError(f"Age must be an integer, got {type(age).__name__}")
    if age < 0 or age > 150:
        raise ValueError(f"Age must be between 0 and 150, got {age}")
    print(f"Age set to {age}")

set_age(25)       # Age set to 25
set_age(-1)       # ValueError: Age must be between 0 and 150, got -1
set_age("old")    # TypeError: Age must be an integer, got str
return None / -1
✗ Silent failure — caller may not notice
✗ Bad data keeps moving through your program
✗ Bug surfaces somewhere far from the cause
raise ValueError()
✓ Immediate, loud failure
✓ Precise message about what went wrong
✓ Caller must acknowledge and handle it

Catching What You Raise

Functions that raise are meant to be called inside try blocks:

python
def make_username(name):
    """Create a username. Name must be 3-20 characters."""
    if len(name) < 3:
        raise ValueError("Name too short. Minimum 3 characters.")
    if len(name) > 20:
        raise ValueError("Name too long. Maximum 20 characters.")
    return name.lower().replace(" ", "_")

for name in ["Riya", "AI", "Superlongusernamethatfails"]:
    try:
        user = make_username(name)
        print(f"Created: {user}")
    except ValueError as e:
        print(f"Invalid name '{name}': {e}")
output
Created: riya
Invalid name 'AI': Name too short. Minimum 3 characters.
Invalid name 'Superlongusernamethatfails': Name too long. Maximum 20 characters.

Re-raising an Exception

Sometimes you want to log an error but still let it propagate up. Use bare raise inside an except block:

python
def load_config(path):
    try:
        with open(path) as f:
            return f.read()
    except FileNotFoundError:
        print(f"[LOG] Config file missing: {path}")
        raise   # re-raise the same exception to the caller

Real Example — Bank Account Deposit

python
def deposit(balance, amount):
    """Add funds to account. Amount must be positive."""
    if not isinstance(amount, (int, float)):
        raise TypeError("Amount must be a number")
    if amount <= 0:
        raise ValueError(f"Deposit amount must be positive, got {amount}")
    return balance + amount

balance = 1000
for amount in [500, -100, "free money", 250]:
    try:
        balance = deposit(balance, amount)
        print(f"Deposited {amount}. Balance: {balance}")
    except (ValueError, TypeError) as e:
        print(f"Deposit failed: {e}")
output
Deposited 500. Balance: 1500
Deposit failed: Deposit amount must be positive, got -100
Deposit failed: Amount must be a number
Deposited 250. Balance: 1750

"Raise early. The earlier you catch a bad value, the easier the bug is to fix. An error at the function boundary is ten times better than a mysterious crash five steps later."

— ShurAI

🧠 Quiz — Q1

What does raise ValueError("bad input") do?

🧠 Quiz — Q2

Why is raising an exception often better than returning None or -1 on error?

🧠 Quiz — Q3

What does bare raise (with nothing after it) do inside an except block?

🧠 Quiz — Q4

Which exception type should you raise when a function receives the right type but an invalid value (e.g. age = -5)?