Common Python Bugs — Real Examples and Fixes
Python's dynamic nature makes certain bugs extremely common — and easy to miss in code review. These are the bugs that appear most frequently in Python production code, with working examples and exact fixes.
What is a Python bug?
A Python bug is any code defect that causes incorrect behavior at runtime. Unlike compiled languages, Python won't catch most bugs until the specific code path is executed — making thorough testing and static analysis essential.
NoneType AttributeError
Accessing attributes or methods on a value that is None. Python's most frequent runtime error.
❌ Crashes when name is None
user = get_user(id) print(user.name.upper()) # AttributeError if user is None or user.name is None
✅ Safe with optional chaining
user = get_user(id)
print(user.name.upper() if user and user.name else "Unknown")
# Or with walrus operator (Python 3.8+):
if name := user and user.name:
print(name.upper())Mutable Default Argument
Using a mutable object (list, dict, set) as a default parameter value. Shared state across all calls.
❌ List shared between calls
def add_tag(tag, tags=[]):
tags.append(tag)
return tags
add_tag("python") # ['python']
add_tag("django") # ['python', 'django'] — NOT ['django']!✅ None sentinel pattern
def add_tag(tag, tags=None):
if tags is None:
tags = [] # New list created for each call
tags.append(tag)
return tagsOff-by-One in Range / Slicing
Python's range() is exclusive at the end. Forgetting this causes off-by-one errors in loops and slices.
❌ Missing last item
items = [10, 20, 30, 40, 50]
for i in range(0, len(items) - 1): # ← Missing last item (50)
process(items[i])
# Also: slice [1:3] returns indexes 1 and 2, NOT 3✅ Correct range usage
items = [10, 20, 30, 40, 50]
for i in range(len(items)): # 0,1,2,3,4 — all items
process(items[i])
# Even better — iterate directly:
for item in items:
process(item)Catching Too-Broad Exceptions
Using bare except: or except Exception: hides bugs and makes debugging impossible.
❌ Hides all errors silently
try:
data = fetch_data()
result = process(data)
except: # ← Catches EVERYTHING including KeyboardInterrupt
pass # Bug? Network error? You'll never know.✅ Specific exception handling
try:
data = fetch_data()
result = process(data)
except requests.ConnectionError as e:
logger.error(f"Network error: {e}")
raise
except ValueError as e:
logger.error(f"Invalid data: {e}")
return NoneInteger Division Surprise (Python 2 → 3)
Python 3 uses true division for / but code migrated from Python 2 may have unexpected behavior.
❌ Unexpected float result
total = 7 count = 2 average = total / count # 3.5 in Python 3 (was 3 in Python 2) # If you expected integer division: page = offset / page_size # Should use //
✅ Explicit integer division
average = total / count # 3.5 — float division page = offset // page_size # 3 — integer (floor) division remainder = offset % page_size # remainder
Pro tip: Run mypy --strict on your codebase and pylint with default settings. Together they catch 70%+ of common Python bugs before runtime.
Detect Python Bugs Automatically
Paste your code — LearnCodeGuide detects all these issues automatically using GPT-4o + Claude Sonnet. Free to start.
Analyze Python Code →Related Guides
Published by LearnCodeGuide Team · Last reviewed: October 2025