Python Built-in Exceptions
Overview
Python has a rich hierarchy of built-in exceptions that help handle errors and exceptional conditions. Understanding these exceptions is crucial for writing robust, error-handling code.
Exception Hierarchy
BaseException
βββ SystemExit
βββ KeyboardInterrupt
βββ GeneratorExit
βββ Exception
βββ StopIteration
βββ StopAsyncIteration
βββ ArithmeticError
β βββ FloatingPointError
β βββ OverflowError
β βββ ZeroDivisionError
βββ AssertionError
βββ AttributeError
βββ BufferError
βββ EOFError
βββ ImportError
β βββ ModuleNotFoundError
βββ LookupError
β βββ IndexError
β βββ KeyError
βββ MemoryError
βββ NameError
β βββ UnboundLocalError
βββ OSError
β βββ BlockingIOError
β βββ ChildProcessError
β βββ ConnectionError
β β βββ BrokenPipeError
β β βββ ConnectionAbortedError
β β βββ ConnectionRefusedError
β β βββ ConnectionResetError
β βββ FileExistsError
β βββ FileNotFoundError
β βββ InterruptedError
β βββ IsADirectoryError
β βββ NotADirectoryError
β βββ PermissionError
β βββ ProcessLookupError
β βββ TimeoutError
βββ ReferenceError
βββ RuntimeError
β βββ NotImplementedError
β βββ RecursionError
βββ SyntaxError
β βββ IndentationError
β βββ TabError
βββ SystemError
βββ TypeError
βββ ValueError
β βββ UnicodeError
β βββ UnicodeDecodeError
β βββ UnicodeEncodeError
β βββ UnicodeTranslateError
βββ Warning
βββ DeprecationWarning
βββ PendingDeprecationWarning
βββ RuntimeWarning
βββ SyntaxWarning
βββ UserWarning
βββ FutureWarning
βββ ImportWarning
βββ UnicodeWarning
βββ BytesWarning
βββ ResourceWarning
1. Common Exceptions
ZeroDivisionError
Raised when dividing by zero.
# Example
try:
result = 10 / 0
except ZeroDivisionError as e:
print(f"Error: {e}") # Output: Error: division by zero
# Real-world usage
def calculate_average(total, count):
try:
return total / count
except ZeroDivisionError:
return 0 # Return default when count is zero
print(calculate_average(100, 0)) # Output: 0
TypeError
Raised when an operation is applied to an object of inappropriate type.
# Example
try:
result = "5" + 5
except TypeError as e:
print(f"Error: {e}") # Output: Error: can only concatenate str (not "int") to str
# Real-world usage
def add_numbers(a, b):
try:
return a + b
except TypeError:
# Try converting to numbers
try:
return float(a) + float(b)
except (TypeError, ValueError):
raise TypeError("Arguments must be numeric")
print(add_numbers("10", "20")) # Output: 30.0
ValueError
Raised when a function receives an argument of correct type but inappropriate value.
# Example
try:
number = int("abc")
except ValueError as e:
print(f"Error: {e}") # Output: Error: invalid literal for int() with base 10: 'abc'
# Real-world usage
def parse_age(age_str):
try:
age = int(age_str)
if age < 0 or age > 150:
raise ValueError("Age must be between 0 and 150")
return age
except ValueError as e:
print(f"Invalid age: {e}")
return None
print(parse_age("25")) # Output: 25
print(parse_age("200")) # Output: Invalid age: Age must be between 0 and 150
IndexError
Raised when trying to access an index that doesn't exist.
# Example
try:
my_list = [1, 2, 3]
print(my_list[10])
except IndexError as e:
print(f"Error: {e}") # Output: Error: list index out of range
# Real-world usage
def safe_get(lst, index, default=None):
try:
return lst[index]
except IndexError:
return default
my_list = [1, 2, 3]
print(safe_get(my_list, 1)) # Output: 2
print(safe_get(my_list, 10)) # Output: None
KeyError
Raised when a dictionary key is not found.
# Example
try:
my_dict = {"name": "Alice", "age": 30}
print(my_dict["email"])
except KeyError as e:
print(f"Error: Key {e} not found") # Output: Error: Key 'email' not found
# Real-world usage
def get_user_info(user_data, key, default="N/A"):
try:
return user_data[key]
except KeyError:
return default
user = {"name": "Bob", "age": 25}
print(get_user_info(user, "name")) # Output: Bob
print(get_user_info(user, "email")) # Output: N/A
AttributeError
Raised when an attribute reference or assignment fails.
# Example
try:
my_list = [1, 2, 3]
my_list.append_item(4) # No such method
except AttributeError as e:
print(f"Error: {e}") # Output: Error: 'list' object has no attribute 'append_item'
# Real-world usage
class Person:
def __init__(self, name):
self.name = name
def get_attribute_safe(obj, attr, default=None):
try:
return getattr(obj, attr)
except AttributeError:
return default
p = Person("Alice")
print(get_attribute_safe(p, "name")) # Output: Alice
print(get_attribute_safe(p, "age")) # Output: None
NameError
Raised when a local or global name is not found.
# Example
try:
print(undefined_variable)
except NameError as e:
print(f"Error: {e}") # Output: Error: name 'undefined_variable' is not defined
# Real-world usage
def safe_eval_variable(var_name, context):
try:
return eval(var_name, context)
except NameError:
return f"Variable '{var_name}' not defined"
context = {"x": 10, "y": 20}
print(safe_eval_variable("x", context)) # Output: 10
print(safe_eval_variable("z", context)) # Output: Variable 'z' not defined
2. File and I/O Exceptions
FileNotFoundError
Raised when a file or directory is requested but doesn't exist.
# Example
try:
with open("nonexistent.txt", "r") as f:
content = f.read()
except FileNotFoundError as e:
print(f"Error: {e}") # Output: Error: [Errno 2] No such file or directory: 'nonexistent.txt'
# Real-world usage
def read_config(filename):
try:
with open(filename, "r") as f:
return f.read()
except FileNotFoundError:
print(f"Config file '{filename}' not found, using defaults")
return "{}"
config = read_config("config.json")
PermissionError
Raised when trying to perform an operation without adequate permissions.
# Example
try:
with open("/root/protected.txt", "w") as f:
f.write("test")
except PermissionError as e:
print(f"Error: {e}") # Output: Error: [Errno 13] Permission denied: '/root/protected.txt'
# Real-world usage
def safe_write(filename, content):
try:
with open(filename, "w") as f:
f.write(content)
return True
except PermissionError:
print(f"No permission to write to {filename}")
return False
IsADirectoryError
Raised when a file operation is requested on a directory.
# Example
try:
with open("/tmp", "r") as f:
content = f.read()
except IsADirectoryError as e:
print(f"Error: {e}") # Output: Error: [Errno 21] Is a directory: '/tmp'
EOFError
Raised when input() hits end-of-file condition.
# Example
try:
user_input = input("Enter something: ")
except EOFError:
print("End of input reached")
3. Import Exceptions
ImportError
Raised when an import statement fails.
# Example
try:
import nonexistent_module
except ImportError as e:
print(f"Error: {e}") # Output: Error: No module named 'nonexistent_module'
# Real-world usage
def optional_import(module_name):
try:
return __import__(module_name)
except ImportError:
print(f"Optional module '{module_name}' not available")
return None
requests = optional_import("requests")
if requests:
print("Requests library available")
ModuleNotFoundError
Subclass of ImportError, raised when a module cannot be located.
# Example
try:
from some_package import some_module
except ModuleNotFoundError as e:
print(f"Error: {e}") # Output: Error: No module named 'some_package'
4. Arithmetic Exceptions
OverflowError
Raised when an arithmetic operation is too large to be represented.
# Example (rare in Python 3 due to arbitrary precision integers)
import math
try:
result = math.exp(1000) # e^1000
except OverflowError as e:
print(f"Error: {e}") # Output: Error: math range error
FloatingPointError
Raised when a floating point operation fails.
# Example (requires special configuration)
import numpy as np
np.seterr(all='raise')
try:
result = np.float64(1.0) / np.float64(0.0)
except FloatingPointError as e:
print(f"Error: {e}")
5. Runtime Exceptions
RecursionError
Raised when maximum recursion depth is exceeded.
# Example
def infinite_recursion():
return infinite_recursion()
try:
infinite_recursion()
except RecursionError as e:
print(f"Error: Maximum recursion depth exceeded")
# Real-world usage with proper recursion
def factorial(n, depth=0, max_depth=1000):
if depth > max_depth:
raise RecursionError("Maximum recursion depth for factorial")
if n <= 1:
return 1
return n * factorial(n - 1, depth + 1, max_depth)
try:
print(factorial(5)) # Output: 120
print(factorial(2000)) # Raises RecursionError
except RecursionError as e:
print(f"Error: {e}")
NotImplementedError
Raised when an abstract method requires implementation.
# Example
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def speak(self):
raise NotImplementedError("Subclass must implement speak()")
class Dog(Animal):
def speak(self):
return "Woof!"
# Real-world usage
class PaymentProcessor(ABC):
@abstractmethod
def process_payment(self, amount):
raise NotImplementedError("Must implement process_payment()")
class StripeProcessor(PaymentProcessor):
def process_payment(self, amount):
return f"Processing ${amount} via Stripe"
class PayPalProcessor(PaymentProcessor):
def process_payment(self, amount):
return f"Processing ${amount} via PayPal"
processor = StripeProcessor()
print(processor.process_payment(100)) # Output: Processing $100 via Stripe
MemoryError
Raised when an operation runs out of memory.
# Example
try:
huge_list = [0] * (10 ** 10) # Try to allocate huge list
except MemoryError:
print("Not enough memory to allocate list")
6. Assertion and System Exceptions
AssertionError
Raised when an assert statement fails.
# Example
try:
x = 5
assert x > 10, "x must be greater than 10"
except AssertionError as e:
print(f"Error: {e}") # Output: Error: x must be greater than 10
# Real-world usage
def withdraw(balance, amount):
assert amount > 0, "Withdrawal amount must be positive"
assert amount <= balance, "Insufficient funds"
return balance - amount
try:
new_balance = withdraw(100, 150)
except AssertionError as e:
print(f"Withdrawal failed: {e}") # Output: Withdrawal failed: Insufficient funds
SystemExit
Raised by sys.exit().
# Example
import sys
try:
sys.exit(0)
except SystemExit as e:
print(f"Exiting with code: {e.code}") # Output: Exiting with code: 0
KeyboardInterrupt
Raised when user interrupts execution (Ctrl+C).
# Example
try:
while True:
pass # Infinite loop
except KeyboardInterrupt:
print("\nProgram interrupted by user")
7. Syntax Exceptions
SyntaxError
Raised when Python encounters invalid syntax.
# This will raise SyntaxError at parse time, can't be caught normally
# if True
# print("Missing colon")
# Real-world usage with eval/exec
try:
eval("if True print('test')")
except SyntaxError as e:
print(f"Syntax error in expression: {e}")
IndentationError
Raised when indentation is incorrect.
# Real-world usage with exec
try:
code = """
def test():
print("Bad indentation")
"""
exec(code)
except IndentationError as e:
print(f"Indentation error: {e}")
TabError
Raised when mixing tabs and spaces inconsistently.
# Example with exec
try:
code = "def test():\n\tpass\n pass" # Mixed tabs and spaces
exec(code)
except TabError as e:
print(f"Tab error: {e}")
8. Unicode Exceptions
UnicodeDecodeError
Raised when Unicode decoding fails.
# Example
try:
byte_string = b'\xff\xfe'
text = byte_string.decode('utf-8')
except UnicodeDecodeError as e:
print(f"Error: Cannot decode bytes")
# Real-world usage
def safe_decode(byte_data, encoding='utf-8'):
try:
return byte_data.decode(encoding)
except UnicodeDecodeError:
return byte_data.decode(encoding, errors='replace')
data = b'\xff\xfe'
print(safe_decode(data)) # Uses replacement character
UnicodeEncodeError
Raised when Unicode encoding fails.
# Example
try:
text = "Hello δΈη"
encoded = text.encode('ascii')
except UnicodeEncodeError as e:
print(f"Error: Cannot encode to ASCII")
# Real-world usage
def safe_encode(text, encoding='utf-8'):
try:
return text.encode(encoding)
except UnicodeEncodeError:
return text.encode(encoding, errors='replace')
9. Connection and Network Exceptions
ConnectionError
Base class for connection-related errors.
# Example
import socket
try:
sock = socket.socket()
sock.connect(("nonexistent.example.com", 80))
except ConnectionError as e:
print(f"Connection error: {e}")
TimeoutError
Raised when an operation times out.
# Example
import socket
try:
sock = socket.socket()
sock.settimeout(1)
sock.connect(("192.0.2.1", 80)) # Non-routable address
except TimeoutError:
print("Connection timed out")
BrokenPipeError
Raised when writing to a pipe/socket that's been closed.
# Example
import socket
try:
sock = socket.socket()
sock.close()
sock.send(b"data")
except BrokenPipeError:
print("Broken pipe - connection closed")
10. Custom Exception Handling Pattern
Best Practices Example
class ValidationError(Exception):
"""Custom exception for validation errors"""
pass
class DatabaseError(Exception):
"""Custom exception for database errors"""
pass
class UserService:
def create_user(self, username, email):
# Validation
if not username:
raise ValidationError("Username cannot be empty")
if "@" not in email:
raise ValidationError("Invalid email format")
# Simulate database operation
try:
# Database logic here
if username == "admin":
raise DatabaseError("Username 'admin' is reserved")
return {"username": username, "email": email}
except DatabaseError as e:
print(f"Database error: {e}")
raise
# Usage
service = UserService()
try:
user = service.create_user("john", "john@example.com")
print(f"User created: {user}")
except ValidationError as e:
print(f"Validation failed: {e}")
except DatabaseError as e:
print(f"Database operation failed: {e}")
except Exception as e:
print(f"Unexpected error: {e}")
11. Exception Handling Patterns
Multiple Exceptions
def process_data(data):
try:
# Multiple operations that can fail
value = int(data['value'])
result = 100 / value
items = data['items']
first_item = items[0]
return first_item
except (KeyError, IndexError) as e:
print(f"Data access error: {e}")
return None
except (ValueError, TypeError) as e:
print(f"Type conversion error: {e}")
return None
except ZeroDivisionError:
print("Cannot divide by zero")
return None
except Exception as e:
print(f"Unexpected error: {e}")
return None
Try-Except-Else-Finally
def read_and_process_file(filename):
file_handle = None
try:
file_handle = open(filename, 'r')
content = file_handle.read()
data = int(content)
except FileNotFoundError:
print(f"File {filename} not found")
return None
except ValueError:
print("File content is not a valid number")
return None
else:
# Executes only if no exception occurred
print("File processed successfully")
return data * 2
finally:
# Always executes, cleanup code
if file_handle:
file_handle.close()
print("File closed")
Context Managers (Automatic Cleanup)
# Better approach using context manager
def read_and_process_file(filename):
try:
with open(filename, 'r') as f:
content = f.read()
data = int(content)
return data * 2
except FileNotFoundError:
print(f"File {filename} not found")
return None
except ValueError:
print("File content is not a valid number")
return None
# File automatically closed, no finally needed
12. Warning Categories
Warnings are not exceptions but alert users to potential issues.
import warnings
# DeprecationWarning
warnings.warn("This function is deprecated", DeprecationWarning)
# UserWarning
warnings.warn("This is a user warning", UserWarning)
# FutureWarning
warnings.warn("This behavior will change in future", FutureWarning)
# Custom warning handling
def risky_operation():
warnings.warn("This operation is risky", RuntimeWarning)
return "result"
# Filter warnings
warnings.filterwarnings('ignore', category=RuntimeWarning)
result = risky_operation() # Warning suppressed
Summary
Exception Handling Best Practices
- Be Specific: Catch specific exceptions rather than using bare
except: - Use Finally: For cleanup operations that must always execute
- Context Managers: Use
withstatements for resource management - Custom Exceptions: Create custom exceptions for application-specific errors
- Don't Silence: Avoid catching exceptions without handling them
- Log Errors: Always log unexpected exceptions for debugging
- Fail Fast: Raise exceptions early when invalid state is detected
- Document: Document what exceptions your functions can raise
Common Exception Categories
| Category | Exceptions | Use Case |
|---|---|---|
| Value Errors | ValueError, TypeError | Invalid arguments or data types |
| Lookup Errors | KeyError, IndexError, AttributeError | Missing keys, indices, or attributes |
| I/O Errors | FileNotFoundError, PermissionError, EOFError | File and I/O operations |
| Arithmetic | ZeroDivisionError, OverflowError | Mathematical operations |
| Runtime | RecursionError, MemoryError | Runtime resource issues |
| Import | ImportError, ModuleNotFoundError | Module loading issues |
| Network | ConnectionError, TimeoutError | Network operations |
| System | SystemExit, KeyboardInterrupt | System-level events |
Quick Reference
# Basic exception handling
try:
risky_operation()
except SpecificError as e:
handle_error(e)
except AnotherError:
handle_another_error()
else:
success_action()
finally:
cleanup()
# Raising exceptions
raise ValueError("Invalid value")
raise CustomError("Something went wrong") from original_exception
# Re-raising
try:
operation()
except SomeError:
log_error()
raise # Re-raise same exception
# Custom exceptions
class MyCustomError(Exception):
pass