PyGuide

Learn Python with practical tutorials and code examples

Complete Guide: What Error to Raise in Python

Understanding what error to raise in Python is essential for writing robust, maintainable code. This comprehensive guide walks you through the decision-making process and teaches you when to use each type of exception with practical examples.

Understanding Python's Exception Hierarchy #

Before diving into what error to raise, it's important to understand Python's built-in exception hierarchy. All exceptions inherit from BaseException, with most custom exceptions inheriting from Exception.

# Exception hierarchy visualization
try:
    # Your code here
    pass
except Exception as e:
    # Catches most exceptions
    print(f"Caught exception: {type(e).__name__}")

Step 1: Identify the Problem Type #

The first step in determining what error to raise is identifying what type of problem occurred. Here's a systematic approach:

Data Type Issues #

When the problem is related to incorrect data types, use TypeError:

🐍 Try it yourself

Output:
Click "Run Code" to see the output

Data Value Issues #

When the data type is correct but the value is inappropriate, use ValueError:

🐍 Try it yourself

Output:
Click "Run Code" to see the output

Step 2: Consider Object State and Attributes #

Missing Attributes #

Use AttributeError when an object lacks required attributes or methods:

class Car:
    def __init__(self, make, model):
        self.make = make
        self.model = model
    
    def get_license_plate(self):
        if not hasattr(self, 'license_plate'):
            raise AttributeError("Car has no license plate assigned")
        return self.license_plate
    
    def assign_license_plate(self, plate):
        self.license_plate = plate

# Example usage
my_car = Car("Toyota", "Camry")
try:
    plate = my_car.get_license_plate()
except AttributeError as e:
    print(f"Missing attribute: {e}")
    # Assign license plate and try again
    my_car.assign_license_plate("ABC-123")
    print(f"License plate: {my_car.get_license_plate()}")

Invalid Object State #

Use RuntimeError for problems that occur during runtime due to invalid state:

🐍 Try it yourself

Output:
Click "Run Code" to see the output

Step 3: Handle Collection and Key Operations #

Dictionary Key Errors #

Use KeyError when accessing non-existent dictionary keys:

🐍 Try it yourself

Output:
Click "Run Code" to see the output

Index Errors for Sequences #

Use IndexError when accessing invalid sequence indices:

def get_list_item(items, index):
    """Safely get item from list with custom error message"""
    if not isinstance(items, (list, tuple)):
        raise TypeError("Expected list or tuple")
    if not isinstance(index, int):
        raise TypeError("Index must be an integer")
    if index >= len(items) or index < -len(items):
        raise IndexError(f"Index {index} out of range for list of length {len(items)}")
    return items[index]

# Example usage
my_list = ["apple", "banana", "cherry"]
try:
    item = get_list_item(my_list, 5)
except IndexError as e:
    print(f"Index error: {e}")

Step 4: Create Custom Exceptions for Domain-Specific Problems #

When built-in exceptions don't clearly represent your specific problem domain, create custom exceptions:

🐍 Try it yourself

Output:
Click "Run Code" to see the output

Step 5: Best Practices for Error Messages #

Write Clear, Actionable Error Messages #

Good error messages should tell the user:

  1. What went wrong
  2. What was expected
  3. How to fix it (when possible)

🐍 Try it yourself

Output:
Click "Run Code" to see the output

Common Mistakes to Avoid #

Don't Use Generic Exceptions for Specific Problems #

# Bad: Too generic
def divide(a, b):
    if b == 0:
        raise Exception("Error occurred")  # Too vague!

# Good: Specific and clear
def divide(a, b):
    if not isinstance(a, (int, float)) or not isinstance(b, (int, float)):
        raise TypeError("Both arguments must be numbers")
    if b == 0:
        raise ZeroDivisionError("Cannot divide by zero")
    return a / b

Don't Raise the Wrong Exception Type #

# Bad: Using ValueError for type issues
def process_list(items):
    if not isinstance(items, list):
        raise ValueError("Not a list")  # Wrong exception type!

# Good: Using correct exception type
def process_list(items):
    if not isinstance(items, list):
        raise TypeError("Expected list, got {type(items).__name__}")

Decision Flow Chart #

Here's a simple decision process for what error to raise in Python:

  1. Wrong data type?TypeError
  2. Right type, wrong value?ValueError
  3. Missing attribute/method?AttributeError
  4. Missing dictionary key?KeyError
  5. Invalid sequence index?IndexError
  6. Invalid runtime state?RuntimeError
  7. Not implemented yet?NotImplementedError
  8. Domain-specific problem? → Custom exception

Summary #

Knowing what error to raise in Python improves code maintainability and debugging efficiency. Remember these key principles:

  • Choose specific exception types over generic ones
  • Write clear, actionable error messages
  • Create custom exceptions for domain-specific problems
  • Consider the exception hierarchy when catching errors
  • Always provide context about what went wrong and why

By following this guide, you'll write more robust Python applications that are easier to debug and maintain.

Next Steps #