# How to Fix Python IndexError: list index out of range

The IndexError occurs when you try to access an element at a position that does not exist in a list, tuple, or other sequence. This typically happens when accessing the wrong index or when the sequence is shorter than expected.

Error Patterns

Basic IndexError

text
Traceback (most recent call last):
  File "app.py", line 3, in <module>
    item = items[5]
IndexError: list index out of range

Empty List Access

text
Traceback (most recent call last):
  File "app.py", line 2, in <module>
    first = items[0]
IndexError: list index out of range

String IndexError

text
Traceback (most recent call last):
  File "app.py", line 4, in <module>
    char = text[100]
IndexError: string index out of range

Tuple IndexError

text
Traceback (most recent call last):
  File "app.py", line 3, in <module>
    value = coordinates[3]
IndexError: tuple index out of range

Common Causes

  1. 1.Empty list - Accessing index from empty sequence
  2. 2.Off-by-one errors - Using len(items) instead of len(items) - 1
  3. 3.Dynamic list changes - List was modified, now shorter
  4. 4.Wrong assumptions - Expecting list to have certain length
  5. 5.Loop index errors - Loop iterating beyond list bounds
  6. 6.Conditional access - Accessing index without checking length

Diagnosis Steps

Step 1: Check List Length

```python # Always check length before accessing items = get_items() print(f"List length: {len(items)}") print(f"List contents: {items}")

# Then access safely if len(items) > 5: print(items[5]) ```

Step 2: Check Index Value

```python # Verify the index you're trying to access index = 10 items = [1, 2, 3, 4, 5]

print(f"Attempting to access index {index}") print(f"Valid indices: 0 to {len(items) - 1}")

if 0 <= index < len(items): print(items[index]) else: print(f"Index {index} is out of bounds") ```

Step 3: Find Where List Is Modified

```python # Trace where the list gets modified def trace_list_modifications(items): original_len = len(items) print(f"Initial length: {original_len}")

# Process items, print length after each modification # Watch for: pop(), remove(), clear(), del, assignment return items ```

Solutions

Solution 1: Check Length Before Access

```python # Problem: Accessing without checking items = [] first = items[0] # IndexError on empty list

# Fix: Check length items = [] if len(items) > 0: first = items[0] else: first = None # Or handle empty case

# Fix: Use conditional expression first = items[0] if items else None ```

Solution 2: Handle Off-by-One Errors

```python # Problem: Last element access items = [1, 2, 3, 4, 5] last = items[5] # IndexError: index 5 doesn't exist (0-4 are valid)

# Fix: Use len() - 1 for last element last = items[len(items) - 1] # items[4] = 5

# Better: Use negative indexing last = items[-1] # Last element second_last = items[-2] # Second to last

# Safely with negative index check if len(items) > 0: last = items[-1] ```

Solution 3: Use try/except for Safe Access

```python # Fix: Wrap access in try/except items = [1, 2, 3]

try: value = items[10] except IndexError: value = None # Handle missing element print("Index out of range, using default")

# For function returning list element def safe_get(items, index, default=None): try: return items[index] except IndexError: return default

value = safe_get(items, 10, "default") ```

Solution 4: Use Slice Instead of Index

```python # Problem: Single element access items = [1, 2, 3] item = items[5] # IndexError

# Fix: Use slice (returns empty list if out of range) item = items[5:6] # Returns [] instead of raising IndexError if item: result = item[0] else: result = None

# Get first element safely first = items[:1] # [] if empty, [first] otherwise if first: print(first[0]) ```

Solution 5: Iterate Safely

```python # Problem: Manual index iteration items = [1, 2, 3] for i in range(len(items) + 1): # Wrong: +1 causes IndexError print(items[i])

# Fix: Iterate directly over items for item in items: print(item) # No IndexError possible

# Fix: Use enumerate if you need index for i, item in enumerate(items): print(f"Index {i}: {item}")

# Fix: Check bounds in manual iteration for i in range(len(items)): # Correct range print(items[i]) ```

Solution 6: Handle Split Results

```python # Problem: Split might not return expected parts text = "hello" parts = text.split(":") first = parts[1] # IndexError: parts has only 1 element

# Fix: Check parts length text = "hello" parts = text.split(":") if len(parts) > 1: first = parts[1] else: first = ""

# Fix: Use maxsplit and defaults text = "hello" first, second = text.split(":", 1) if ":" in text else (text, "") ```

Solution 7: Handle CSV Row Access

```python import csv

# Problem: Rows might have different column counts with open("data.csv") as f: reader = csv.reader(f) for row in reader: email = row[3] # IndexError if row has fewer columns

# Fix: Check row length with open("data.csv") as f: reader = csv.reader(f) for row in reader: email = row[3] if len(row) > 3 else ""

# Fix: Use helper function def get_column(row, index, default=""): return row[index] if len(row) > index else default

email = get_column(row, 3) ```

Solution 8: Handle API Response Arrays

```python import requests

response = requests.get("https://api.example.com/items") items = response.json()

# Problem: Expecting certain structure first_item = items[0] # IndexError if empty response

# Fix: Validate response structure if items and isinstance(items, list): first_item = items[0] if len(items) > 0 else None else: first_item = None print("Unexpected response format") ```

Safe Access Patterns

Safe First/Last Access

```python def safe_first(items, default=None): """Get first element safely.""" return items[0] if items else default

def safe_last(items, default=None): """Get last element safely.""" return items[-1] if items else default

def safe_get(items, index, default=None): """Get element at index safely.""" try: return items[index] except (IndexError, TypeError): return default

# Usage items = [1, 2, 3] print(safe_first(items)) # 1 print(safe_last(items)) # 3 print(safe_get(items, 10)) # None print(safe_first([])) # None ```

Slice Patterns

```python # Slices never raise IndexError items = [1, 2, 3]

# Get first N elements safely first_three = items[:3] # [1, 2, 3] first_five = items[:5] # [1, 2, 3] - returns all available

# Get last N elements safely last_three = items[-3:] # [1, 2, 3] last_five = items[-5:] # [1, 2, 3] - returns all available

# Middle elements middle = items[1:2] # [2] ```

Negative Index Handling

```python # Negative indices work from end items = [1, 2, 3, 4, 5] print(items[-1]) # 5 (last) print(items[-2]) # 4 (second last) print(items[-6]) # IndexError (too negative)

# Safe negative index def safe_negative(items, neg_index, default=None): if len(items) >= abs(neg_index): return items[neg_index] return default

value = safe_negative(items, -10) # None instead of IndexError ```

Common IndexError Patterns

Parsing Fixed Format Data

```python def parse_log_line(line): """Parse log line safely.""" parts = line.split()

# Use safe access for each field timestamp = parts[0] if len(parts) > 0 else "" level = parts[1] if len(parts) > 1 else "INFO" message = " ".join(parts[2:]) if len(parts) > 2 else ""

return { "timestamp": timestamp, "level": level, "message": message } ```

Processing Command Arguments

```python import sys

def get_arg(index, default=None): """Get command line argument safely.""" return sys.argv[index] if len(sys.argv) > index else default

# Usage filename = get_arg(1, "default.txt") mode = get_arg(2, "read") ```

Matrix/2D Array Access

```python matrix = [[1, 2, 3], [4, 5]]

# Problem: Inconsistent row lengths value = matrix[0][5] # IndexError value = matrix[2][0] # IndexError

# Fix: Check both dimensions def safe_matrix_get(matrix, row, col, default=None): try: return matrix[row][col] except IndexError: return default

value = safe_matrix_get(matrix, 0, 5, 0) ```

Prevention Tips

  1. 1.Check length before access - if len(items) > index
  2. 2.Use slice syntax - Returns empty instead of raising error
  3. 3.Iterate directly - Don't use range(len(items)) unless necessary
  4. 4.Validate input - Check data structure assumptions
  5. 5.Use helper functions - Centralize safe access patterns

```python # Good pattern: Defensive list handling def process_items(items): if not items: # Handle empty return []

# Safe access patterns throughout first = items[0] if items else None rest = items[1:] # Safe slice

return process(first, rest) ```

  • KeyError - Dictionary key not found
  • TypeError - Object doesn't support indexing
  • ValueError - Wrong value type for operation
  • AttributeError - Object doesn't have the attribute