The IndexError: list index out of range error means your Python code is trying to access an element in a list using an index that doesn’t exist. This usually happens when the index is negative, or greater than or equal to the length of the list.

Here are the most common culprits and how to fix them:

1. Off-by-One Error in Loops:

  • Diagnosis: You’re iterating over a list and using the loop counter as an index, but the loop runs one iteration too many.
  • Command/Check:
    my_list = [10, 20, 30]
    for i in range(len(my_list) + 1): # Incorrect: range goes up to len(my_list)
        print(my_list[i])
    
  • Fix: Adjust the range to go up to len(my_list).
    my_list = [10, 20, 30]
    for i in range(len(my_list)): # Correct: range goes up to len(my_list) - 1
        print(my_list[i])
    
  • Why it works: range(n) generates numbers from 0 up to n-1. List indices are also 0-based. If len(my_list) is 3, valid indices are 0, 1, and 2. range(3) gives 0, 1, 2. range(4) gives 0, 1, 2, 3, and my_list[3] is out of bounds.

2. Accessing an Empty List:

  • Diagnosis: Your code assumes a list has elements when it’s actually empty.
  • Command/Check:
    data = []
    print(data[0])
    
  • Fix: Always check if a list is empty before accessing its elements.
    data = []
    if data: # This checks if the list is not empty
        print(data[0])
    else:
        print("The list is empty.")
    
  • Why it works: An empty list evaluates to False in a boolean context. The if data: condition prevents execution of data[0] if the list is empty.

3. Incorrect Index Calculation:

  • Diagnosis: You’re calculating an index based on some logic, and that calculation results in an invalid index. This is common with user input or dynamic data.
  • Command/Check:
    values = [1, 2, 3, 4, 5]
    user_input = "3" # Imagine this came from input()
    index_to_access = int(user_input)
    print(values[index_to_access])
    
    If user_input was "5", int("5") is 5, and values[5] would fail.
  • Fix: Validate the calculated index against the list’s bounds.
    values = [1, 2, 3, 4, 5]
    user_input = "5"
    index_to_access = int(user_input)
    if 0 <= index_to_access < len(values):
        print(values[index_to_access])
    else:
        print(f"Index {index_to_access} is out of bounds for list of length {len(values)}.")
    
  • Why it works: This explicitly checks if the computed index_to_access falls within the valid range of 0 to len(values) - 1 before attempting the access.

4. Using Negative Indices Incorrectly:

  • Diagnosis: While negative indices are valid for accessing elements from the end of a list (e.g., my_list[-1] is the last element), using a negative index that is "too negative" will cause an IndexError.
  • Command/Check:
    my_list = ['a', 'b', 'c']
    print(my_list[-4]) # This will fail
    
  • Fix: Ensure negative indices are within the valid range of -len(my_list) to -1.
    my_list = ['a', 'b', 'c']
    index_to_access = -4
    if -len(my_list) <= index_to_access < 0:
        print(my_list[index_to_access])
    else:
        print(f"Negative index {index_to_access} is out of bounds for list of length {len(my_list)}.")
    
  • Why it works: For a list of length 3, valid negative indices are -1, -2, and -3. An index of -4 is attempting to go beyond the first element. The check ensures the negative index is not smaller than -len(my_list).

5. Modifying a List While Iterating:

  • Diagnosis: Removing elements from a list while iterating over it with indices can cause subsequent indices to point to the wrong elements or go out of bounds.
  • Command/Check:
    numbers = [1, 2, 3, 4, 5]
    for i in range(len(numbers)):
        if numbers[i] % 2 == 0:
            numbers.pop(i) # This is problematic
    print(numbers)
    
    When numbers[i] is 2 (at index 1), pop(1) removes 2. The list becomes [1, 3, 4, 5]. The loop then moves to i=2, which now points to 4 (it was originally at index 3). If numbers[i] was 4 (at index 2), and pop(2) removes it, the list becomes [1, 3, 5]. The loop then moves to i=3, which is out of bounds.
  • Fix: Iterate over a copy of the list, or iterate in reverse, or build a new list.
    numbers = [1, 2, 3, 4, 5]
    for i in range(len(numbers) - 1, -1, -1): # Iterate backwards
        if numbers[i] % 2 == 0:
            numbers.pop(i)
    print(numbers) # Output: [1, 3, 5]
    
    Or, build a new list:
    numbers = [1, 2, 3, 4, 5]
    odd_numbers = [num for num in numbers if num % 2 != 0]
    print(odd_numbers) # Output: [1, 3, 5]
    
  • Why it works: Iterating backward ensures that removing an element doesn’t affect the indices of elements you haven’t visited yet. Building a new list avoids modification altogether.

6. Indexing into a List of Lists with Incorrect Dimensions:

  • Diagnosis: When dealing with nested lists (like a 2D matrix), you might provide too many or too few indices, or indices that are out of bounds for the inner lists.
  • Command/Check:
    matrix = [[1, 2], [3, 4]]
    print(matrix[0][2]) # Will fail if inner list only has 2 elements
    print(matrix[2][0]) # Will fail if outer list only has 2 elements
    
  • Fix: Ensure you have the correct number of indices and that each index is within the bounds of its respective list.
    matrix = [[1, 2], [3, 4]]
    row_index = 0
    col_index = 1
    if 0 <= row_index < len(matrix) and 0 <= col_index < len(matrix[row_index]):
        print(matrix[row_index][col_index])
    else:
        print("Invalid indices for matrix.")
    
  • Why it works: This performs bounds checking at each level of nesting, ensuring that both the row and column indices are valid for the given matrix structure.

The next error you’ll likely encounter after fixing IndexError is a KeyError if you’re using dictionaries, or a TypeError if you’re trying to perform operations on incompatible data types.

Want structured learning?

Take the full Python course →