foreach-ui logo
codeLanguages
account_treeDSA

Quick Actions

quizlock Random Quiz
trending_uplock Progress
  • 1
  • 2
  • 3
  • 4
  • quiz
Python
  • Use raise to throw exceptions
  • Create custom exception classes
  • Know when to raise vs return errors
  • Re-raise exceptions when needed

Raising Exceptions

So far, you've learned to catch errors. But what if YOUR code detects a problem? You need to signal to the rest of the program: "Stop! Something is wrong!" This is called raising (or throwing) an exception.

Think of it like being a referee in a game. When you see a foul, you blow the whistle and stop play. Raising an exception is your program's whistle!


The raise Statement

# Basic raise
raise ValueError("Age cannot be negative")

# With more context
age = -5
if age < 0:
    raise ValueError(f"Age must be positive, got {age}")

When the Exception is Raised


                    What Happens When You Raise                   

                                                                  
   def validate_age(age):                                        
       if age < 0:                                               
           raise ValueError("Negative!")  ← Program stops here   
       return age  ← This never runs if age < 0                  
                                                                  
   try:                                                          
       validate_age(-5)                                          
   except ValueError as e:      ← Exception caught here          
       print(f"Error: {e}")                                      
                                                                  
   # If not caught, program crashes with traceback               
                                                                  

Choosing the Right Exception

Use Python's built-in exceptions when appropriate:

Exception When to Use
ValueError Wrong value (right type, wrong value)
TypeError Wrong type
KeyError Missing dictionary key
IndexError Index out of range
FileNotFoundError File doesn't exist
PermissionError No permission
RuntimeError General runtime error
def divide(a, b):
    if not isinstance(b, (int, float)):
        raise TypeError(f"b must be a number, got {type(b)}")
    if b == 0:
        raise ValueError("Cannot divide by zero")
    return a / b

Custom Exceptions

Create your own exception types for specific errors:

# Simple custom exception
class InsufficientFundsError(Exception):
    """Raised when account has insufficient funds."""
    pass

# Custom exception with extra data
class ValidationError(Exception):
    """Raised when validation fails."""
    def __init__(self, field, message):
        self.field = field
        self.message = message
        super().__init__(f"{field}: {message}")

Using Custom Exceptions

class BankAccount:
    def __init__(self, balance=0):
        self.balance = balance
    
    def withdraw(self, amount):
        if amount > self.balance:
            raise InsufficientFundsError(
                f"Cannot withdraw ${amount}. Balance: ${self.balance}"
            )
        self.balance -= amount
        return amount

# Usage
account = BankAccount(100)
try:
    account.withdraw(150)
except InsufficientFundsError as e:
    print(f"Transaction failed: {e}")

Re-raising Exceptions

Sometimes you want to catch an exception, do something, then let it continue:

def process_file(filename):
    try:
        with open(filename) as f:
            return f.read()
    except FileNotFoundError:
        print(f"Logging: File {filename} not found")
        raise  # Re-raise the same exception

# The caller still needs to handle it
try:
    data = process_file("missing.txt")
except FileNotFoundError:
    print("Handling in caller")

Exception Chaining

Show what caused an exception:

def load_config(filename):
    try:
        with open(filename) as f:
            return parse_config(f.read())
    except FileNotFoundError as e:
        raise ConfigError(f"Cannot load config") from e

Practical Examples

Example 1: Input Validation

def create_user(name, age, email):
    """Create a user with validation."""
    # Validate name
    if not name or not name.strip():
        raise ValueError("Name cannot be empty")
    
    # Validate age
    if not isinstance(age, int):
        raise TypeError("Age must be an integer")
    if age < 0 or age > 150:
        raise ValueError(f"Age must be 0-150, got {age}")
    
    # Validate email
    if "@" not in email:
        raise ValueError("Invalid email format")
    
    return {"name": name.strip(), "age": age, "email": email}

# Usage
try:
    user = create_user("Alice", 25, "alice@example.com")
    print(f"Created: {user}")
except (ValueError, TypeError) as e:
    print(f"Validation failed: {e}")

Example 2: API Response Handler

class APIError(Exception):
    """Base exception for API errors."""
    pass

class NotFoundError(APIError):
    """Resource not found."""
    pass

class AuthenticationError(APIError):
    """Authentication failed."""
    pass

def handle_response(status_code, data):
    """Handle API response and raise appropriate errors."""
    if status_code == 200:
        return data
    elif status_code == 401:
        raise AuthenticationError("Invalid credentials")
    elif status_code == 404:
        raise NotFoundError("Resource not found")
    else:
        raise APIError(f"API error: {status_code}")

When to Raise vs Return


               Raise Exception vs Return Value                    

                                                                  
   USE EXCEPTIONS when:                                          
   • Something unexpected/exceptional happened                    
   • The function cannot fulfill its contract                    
   • The caller MUST handle it (can't be ignored)                
                                                                  
   RETURN VALUES when:                                           
   • The outcome is an expected possibility                      
   • Finding "nothing" is a valid result                         
   • The caller might reasonably ignore it                       
                                                                  
   Example:                                                       
   • find_user(id) → return None if not found (expected)         
   • get_user(id) → raise NotFoundError (must exist)             
                                                                  

Key Takeaways


                   Remember These Points                          

                                                                  
   raise ExceptionType("message")                              
     Signals that something went wrong                            
                                                                  
   Use appropriate built-in exceptions                         
     ValueError, TypeError, KeyError, etc.                        
                                                                  
   Create custom exceptions for domain-specific errors         
     class MyError(Exception): pass                               
                                                                  
   Use bare 'raise' to re-raise current exception              
                                                                  
   Raise for exceptional cases, return for expected outcomes   
                                                                  

What's Next?

You can now create and throw errors! But how do you find bugs in the first place? In the next lesson, we'll learn debugging basics – techniques to find and fix problems in your code.

© 2026 forEach. All rights reserved.

Privacy Policy•Terms of Service