Comments
Sign in to join the conversation
Sign in to join the conversation
One of the most common errors Python beginners encounter is:
IndexError: list index out of range
This error is frustrating because it crashes your program, but the good news is that it's usually easy to fix once you understand what's happening.
An IndexError occurs when you try to access a position (index) in a list that doesn't exist. Think of it like trying to open the 10th door in a hallway that only has 5 doors - the door simply isn't there.
In Python, list indices start at 0, so a list with 5 elements has valid indices: 0, 1, 2, 3, 4.
Before we dive into errors, let's review how Python indexes lists:
fruits = ['apple', 'banana', 'cherry']
# Valid indices: 0, 1, 2
print(fruits[0]) # 'apple'
print(fruits[1]) # 'banana'
print(fruits[2]) # 'cherry'
# Negative indices (count from the end)
print(fruits[-1]) # 'cherry' (last item)
print(fruits[-2]) # 'banana' (second to last)
print(fruits[-3]) # 'apple' (third to last)
# Invalid indices
print(fruits[3]) # IndexError! Only indices 0-2 exist
print(fruits[-4]) # IndexError! Only -3 to -1 exist
Key Point: If a list has n elements, valid positive indices are 0 to n-1, and valid negative indices are -n to -1.
The most common mistake - forgetting that lists start at index 0.
names = ['Alice', 'Bob', 'Charlie']
# WRONG: Trying to get the "3rd element" at index 3
print(names[3])
# IndexError: list index out of range
Why it fails: The list has 3 elements (indices 0, 1, 2). There is no index 3.
Fix:
# CORRECT: The "3rd element" is at index 2
print(names[2]) # 'Charlie'
# Or use negative indexing for last element
print(names[-1]) # 'Charlie'
Using the length of the list directly as a loop limit.
numbers = [10, 20, 30, 40]
# WRONG: range(4) gives 0, 1, 2, 3 - the last index is 4, which doesn't exist
for i in range(len(numbers)):
print(numbers[i]) # Works until i=3
print(numbers[i + 1]) # IndexError when i=3!
Fix Option 1: Adjust your range
# CORRECT: Stop one element earlier
for i in range(len(numbers) - 1):
print(numbers[i])
print(numbers[i + 1])
Fix Option 2: Use enumerate() (BETTER)
# BETTER: Iterate over the list directly
for i, num in enumerate(numbers):
print(f"Index {i}: {num}")
Fix Option 3: Use zip() for pairs
# BEST for accessing adjacent elements
for current, next_item in zip(numbers, numbers[1:]):
print(f"Current: {current}, Next: {next_item}")
Trying to access any index of an empty list.
empty_list = []
# WRONG: No elements at all!
first_item = empty_list[0]
# IndexError: list index out of range
Fix: Check if list is empty first
# CORRECT: Check before accessing
if empty_list:
first_item = empty_list[0]
else:
first_item = None # or handle appropriately
# Or use conditional expression
first_item = empty_list[0] if empty_list else None
# Or use try/except
try:
first_item = empty_list[0]
except IndexError:
first_item = None
Removing items from a list while looping through it.
numbers = [1, 2, 3, 4, 5]
# WRONG: Don't modify list while iterating by index
for i in range(len(numbers)):
if numbers[i] % 2 == 0:
numbers.pop(i) # This changes the list size!
# IndexError on later iterations
Why it fails: When you remove an element, the list becomes shorter, but your loop still tries to access the original indices.
Fix Option 1: Iterate backwards
# CORRECT: Go backwards to avoid index shifting issues
for i in range(len(numbers) - 1, -1, -1):
if numbers[i] % 2 == 0:
numbers.pop(i)
Fix Option 2: List comprehension (BEST)
# BETTER: Create new list with only odd numbers
numbers = [num for num in numbers if num % 2 != 0]
Fix Option 3: Create a copy
# CORRECT: Iterate over a copy
for num in numbers[:]: # [:] creates a copy
if num % 2 == 0:
numbers.remove(num)
Working with split strings and assuming indices exist.
# User input might not have the expected format
user_input = "John" # Expected: "FirstName LastName"
# WRONG: Assumes there's always a space and two parts
first_name, last_name = user_input.split()
# ValueError: not enough values to unpack (expected 2, got 1)
# Or accessing by index
parts = user_input.split()
last_name = parts[1] # IndexError if only one word!
Fix: Validate the data first
# CORRECT: Check before unpacking
parts = user_input.split()
if len(parts) >= 2:
first_name = parts[0]
last_name = parts[1]
else:
first_name = parts[0] if parts else "Unknown"
last_name = "Unknown"
# Or use defaults
parts = user_input.split() + ['Unknown'] * 2
first_name, last_name = parts[0], parts[1]
Trying to access indices in multi-dimensional lists without checking dimensions.
matrix = [
[1, 2, 3],
[4, 5, 6]
]
# WRONG: Assumes all rows have same length
print(matrix[0][3]) # IndexError: row 0 only has indices 0-2
# WRONG: Assumes more rows exist
print(matrix[2][0]) # IndexError: only 2 rows (indices 0-1)
Fix: Validate dimensions
# CORRECT: Check both dimensions
row, col = 0, 3
if row < len(matrix) and col < len(matrix[row]):
print(matrix[row][col])
else:
print("Index out of bounds")
Reading lines from a file and assuming a certain number of lines.
# WRONG: Assumes file has at least 10 lines
with open('data.txt', 'r') as f:
lines = f.readlines()
header = lines[0]
data = lines[1:10] # What if file has fewer than 10 lines?
Fix: Check length first
# CORRECT
with open('data.txt', 'r') as f:
lines = f.readlines()
if len(lines) > 0:
header = lines[0]
data = lines[1:min(10, len(lines))]
else:
print("File is empty")
def safe_get(lst, index, default=None):
"""Safely get an item from a list."""
try:
return lst[index]
except IndexError:
return default
# Usage
numbers = [1, 2, 3]
print(safe_get(numbers, 5, "Not found")) # "Not found"
print(safe_get(numbers, 1)) # 2
def get_item(lst, index):
"""Get item with bounds checking."""
if -len(lst) <= index < len(lst):
return lst[index]
else:
raise ValueError(f"Index {index} out of range for list of length {len(lst)}")
# Or without exception
def get_item_safe(lst, index):
if -len(lst) <= index < len(lst):
# Instead of this:
for i in range(len(my_list)):
process(my_list[i])
# Do this:
for item in my_list:
process(item)
# If you need the index too:
for i, item in enumerate(my_list):
print(f"Item {i}: {item}")
my_list = [1, 2, 3]
# Indexing raises error
print(my_list[10]) # IndexError
# Slicing returns empty list or partial results
print(my_list[10:]) # []
print(my_list[:10]) # [1, 2, 3]
print(my_list[1:10]) # [2, 3]
When you get an IndexError, here's how to debug it:
Traceback (most recent call last):
File "script.py", line 15, in <module>
print(data[i])
IndexError: list index out of range
The line number (15) tells you where the error occurred.
# Add debug prints
print(f"List length: {len(my_list)}")
print(f"Trying to access index: {i}")
print(f"List contents: {my_list}")
def process_items(items, start_index):
assert 0 <= start_index < len(items), \
f"start_index {start_index} out of range for list of length {len(items)}"
return items[start_index]
def get_user_selection(options, user_input):
try:
index = int(user_input)
return options[index]
except (ValueError, IndexError):
return "Invalid selection"
Here's a complete example showing best practices:
import csv
def process_csv(filename):
"""Process CSV file with proper error handling."""
try:
with open(filename, 'r') as f:
reader = csv.reader(f)
# Get header (first row)
try:
header = next(reader)
except StopIteration:
print("Error: CSV file is empty")
return
# Process data rows
for row_num, row in enumerate(reader, start=2):
# Validate row has expected number of columns
IndexError occurs when you try to access a list index that doesn't exist. The most common causes:
range(len(list)) carelesslyPrevention strategies:
if len(lst) > indexenumerate() instead of manual indexingRemember: Python's IndexError is helpful - it's telling you that your assumptions about the data structure are wrong. When you see it, check your list length and index values!