10 KiB
10 KiB
Python Error Patterns
Common Python errors with diagnosis and solutions.
Import Errors
ModuleNotFoundError
ModuleNotFoundError: No module named 'package_name'
Causes:
- Package not installed
- Virtual environment not activated
- Wrong Python version
- Typo in module name
Diagnosis:
# Check if package installed
pip show package_name
pip list | grep package
# Check which Python
which python
python --version
# Check if in venv
echo $VIRTUAL_ENV
Solutions:
# Install package
pip install package_name
# If using virtual env
source venv/bin/activate
pip install package_name
# If wrong Python version
python3 -m pip install package_name
pip3 install package_name
ImportError: cannot import name 'X' from 'Y'
ImportError: cannot import name 'MyClass' from 'mymodule'
Causes:
- Name doesn't exist in module
- Circular import
- Module structure changed
- Typo in name
Diagnosis:
# Check what's available
import mymodule
print(dir(mymodule))
# Check if circular import
# Add print at top of each module to see import order
Solutions:
# Circular import fix - move import inside function
def my_function():
from other_module import something
return something()
# Or restructure to avoid circular dependency
Type Errors
TypeError: 'NoneType' object is not subscriptable
TypeError: 'NoneType' object is not subscriptable
Causes:
- Function returned None (forgot return)
- dict.get() returned None
- API response is None
Solutions:
# Add None check
if result is not None:
value = result['key']
# Use default value
value = result.get('key', 'default') if result else 'default'
# Use walrus operator (Python 3.8+)
if (result := get_result()) is not None:
value = result['key']
TypeError: 'X' object is not callable
TypeError: 'str' object is not callable
Causes:
- Variable shadows built-in function
- Missing method parentheses somewhere
- Property accessed as method
Common Culprits:
# Shadowing built-ins - DON'T DO THIS
list = [1, 2, 3] # Shadows list()
str = "hello" # Shadows str()
dict = {'a': 1} # Shadows dict()
type = "my_type" # Shadows type()
id = 123 # Shadows id()
Solutions:
# Rename variables
my_list = [1, 2, 3]
my_str = "hello"
# Or delete the shadow
del list
TypeError: unsupported operand type(s)
TypeError: unsupported operand type(s) for +: 'int' and 'str'
Causes:
- Mixing types in operation
- Unexpected type from input/API
Solutions:
# Convert types explicitly
result = str(number) + text
result = number + int(text)
# Type checking
if isinstance(value, int):
result = value + 10
Attribute Errors
AttributeError: 'NoneType' object has no attribute 'X'
AttributeError: 'NoneType' object has no attribute 'split'
Causes:
- Variable is None when it shouldn't be
- Function returned None
- Failed assignment
Solutions:
# Guard against None
if text is not None:
words = text.split()
# With default
words = (text or "").split()
# Assert early
assert text is not None, "text cannot be None"
AttributeError: module 'X' has no attribute 'Y'
AttributeError: module 'json' has no attribute 'loads'
Causes:
- Local file shadows standard library
- Wrong module imported
- Outdated module version
Diagnosis:
import json
print(json.__file__) # Check which file is loaded
Solutions:
# If local file shadows stdlib
mv json.py my_json_utils.py
rm json.pyc __pycache__/json*
# Check for naming conflicts
ls *.py | grep -E "^(json|os|sys|re|io)\.py$"
Key/Index Errors
KeyError
KeyError: 'username'
Causes:
- Key doesn't exist in dict
- Typo in key name
- Data structure changed
Solutions:
# Use .get() with default
username = data.get('username', 'anonymous')
# Check before access
if 'username' in data:
username = data['username']
# Use defaultdict
from collections import defaultdict
data = defaultdict(str)
IndexError: list index out of range
IndexError: list index out of range
Causes:
- Accessing index beyond list length
- Empty list
- Off-by-one error
Solutions:
# Check length first
if len(my_list) > index:
value = my_list[index]
# Use try/except
try:
value = my_list[index]
except IndexError:
value = default_value
# Safe last element
last = my_list[-1] if my_list else None
Value Errors
ValueError: invalid literal for int()
ValueError: invalid literal for int() with base 10: 'abc'
Causes:
- Non-numeric string to int()
- Float string to int()
- Empty string
Solutions:
# Safe conversion
def safe_int(value, default=0):
try:
return int(value)
except (ValueError, TypeError):
return default
# Check first
if value.isdigit():
number = int(value)
# For floats as strings
number = int(float("3.14")) # -> 3
ValueError: too many values to unpack
ValueError: too many values to unpack (expected 2)
Causes:
- Unpacking mismatch
- Data has more/fewer items than expected
Solutions:
# Use * to capture rest
first, *rest = [1, 2, 3, 4] # first=1, rest=[2,3,4]
first, second, *_ = [1, 2, 3, 4] # Ignore extras
# Check length first
if len(items) >= 2:
a, b = items[0], items[1]
File Errors
FileNotFoundError
FileNotFoundError: [Errno 2] No such file or directory: 'file.txt'
Causes:
- File doesn't exist
- Wrong path (relative vs absolute)
- Typo in filename
Solutions:
from pathlib import Path
# Check existence
path = Path('file.txt')
if path.exists():
content = path.read_text()
# Use absolute path
path = Path(__file__).parent / 'data' / 'file.txt'
# Create if not exists
path.parent.mkdir(parents=True, exist_ok=True)
path.touch()
PermissionError
PermissionError: [Errno 13] Permission denied: '/path/to/file'
Causes:
- No write permission
- File owned by another user
- File is read-only
Diagnosis:
ls -la /path/to/file
stat /path/to/file
Solutions:
# Change permissions
chmod 644 /path/to/file
chmod 755 /path/to/directory
# Change ownership
sudo chown $USER:$USER /path/to/file
# Write to user directory instead
from pathlib import Path
user_dir = Path.home() / '.myapp'
user_dir.mkdir(exist_ok=True)
Encoding Errors
UnicodeDecodeError
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff
Causes:
- File not UTF-8 encoded
- Binary file read as text
- Mixed encodings
Solutions:
# Try different encoding
with open('file.txt', encoding='latin-1') as f:
content = f.read()
# Detect encoding
import chardet
with open('file.txt', 'rb') as f:
result = chardet.detect(f.read())
encoding = result['encoding']
with open('file.txt', encoding=encoding) as f:
content = f.read()
# Ignore errors (lossy)
with open('file.txt', encoding='utf-8', errors='ignore') as f:
content = f.read()
JSON Errors
json.decoder.JSONDecodeError
json.decoder.JSONDecodeError: Expecting value: line 1 column 1
Causes:
- Invalid JSON syntax
- Empty string/file
- HTML returned instead of JSON
Diagnosis:
# Print raw content first
print(repr(response.text))
print(response.text[:100])
Solutions:
# Check before parsing
if response.text:
try:
data = json.loads(response.text)
except json.JSONDecodeError as e:
print(f"Invalid JSON at line {e.lineno}, col {e.colno}")
print(f"Content: {response.text[:200]}")
# Handle HTML error pages
if response.text.startswith('<'):
raise ValueError("Received HTML instead of JSON")
Recursion Errors
RecursionError: maximum recursion depth exceeded
RecursionError: maximum recursion depth exceeded
Causes:
- Infinite recursion
- Missing base case
- Deep data structure
Solutions:
# Increase limit (temporary fix)
import sys
sys.setrecursionlimit(10000)
# Better: convert to iteration
def factorial_iterative(n):
result = 1
for i in range(1, n + 1):
result *= i
return result
# Use @lru_cache for memoization
from functools import lru_cache
@lru_cache(maxsize=None)
def fib(n):
if n < 2:
return n
return fib(n - 1) + fib(n - 2)
Async Errors
RuntimeError: Event loop is closed
RuntimeError: Event loop is closed
Causes:
- Trying to use closed event loop
- Event loop not properly managed
- Windows-specific issue with ProactorEventLoop
Solutions:
# Use asyncio.run() (Python 3.7+)
asyncio.run(main())
# For Windows
if sys.platform == 'win32':
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
# Explicit loop management
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
loop.run_until_complete(main())
finally:
loop.close()
RuntimeError: cannot reuse already awaited coroutine
RuntimeError: cannot reuse already awaited coroutine
Causes:
- Awaiting same coroutine twice
- Storing coroutine in variable and reusing
Solutions:
# Wrong - reusing coroutine
coro = fetch_data()
await coro
await coro # Error!
# Correct - create new coroutine each time
await fetch_data()
await fetch_data()
# Or use asyncio.create_task for concurrent execution
task1 = asyncio.create_task(fetch_data())
task2 = asyncio.create_task(fetch_data())
await asyncio.gather(task1, task2)
Quick Reference Table
| Error | Category | Quick Fix |
|---|---|---|
ModuleNotFoundError |
Import | pip install <pkg> |
ImportError: circular |
Import | Move import inside function |
TypeError: NoneType subscript |
Type | Add if x is not None check |
TypeError: not callable |
Type | Check for shadowed built-ins |
AttributeError: NoneType |
Attribute | Guard against None |
KeyError |
Dict | Use .get() with default |
IndexError |
List | Check len() first |
ValueError: int() |
Value | Use try/except |
FileNotFoundError |
File | Use Path.exists() check |
UnicodeDecodeError |
Encoding | Try encoding='latin-1' |
JSONDecodeError |
JSON | Check response content first |
RecursionError |
Recursion | Convert to iteration |