IndexError —
The index walked one step too far.
You asked a list for an index that doesn't exist — usually one past the last valid position. It's the single most common off-by-one bug in Python, and it almost always comes from a loop bound that's wrong by exactly one. Watch the index march past the list's last valid position on LearnBug and see precisely where it goes wrong.
What is IndexError?
Python raises IndexError: list index out of range when you access my_list[i] with an i that's outside the valid range. For a list of length n, valid indices run from 0 to n-1 — asking for index n (or higher, or a negative index beyond -n) is always out of bounds.
Why visualization helps
The bug is in the loop's boundary, not in whatever line the traceback points to — and boundaries are exactly what's hardest to eyeball correctly. LearnBug shows the index value at every iteration next to the list itself, so you can see the exact iteration where it steps past the last valid position.
Three ways an index goes out of bounds
range(len(list) + 1)
Adding +1 (often by accident, or from confusing "number of elements" with "last valid index") makes the loop try one index past the end.
list[i + 1] near the end of a loop
Comparing each element to its neighbor (common in sorting algorithms) reads one index ahead — on the very last element, that neighbor doesn't exist.
An empty or shorter-than-expected list
Accessing list[0] assuming there's at least one element — but the list turned out to be empty, so even index 0 is out of range.
Looping through [85, 92, 78] with range(len(scores) + 1)
i=0: scores[0] = 85 — valid, prints fine
The list has 3 elements, valid indices 0, 1, 2. Index 0 is well within range.
i=1, i=2: scores[1]=92, scores[2]=78 — both still valid
Index 2 is the last valid index for a 3-element list. Everything up to here is fine.
i=3: because range(len(scores) + 1) = range(4), the loop tries one more index
len(scores) is 3, so range(len(scores) + 1) produces 0, 1, 2, 3 — four values for a three-element list. Index 3 doesn't exist.
scores[3] raises IndexError immediately
Python checks bounds on every single index access — there's no silent "return None" or default value, it raises right at the moment of access.
Result: IndexError: list index out of range ✗ — the +1 in the range call is the bug
The broken code, and the traceback it produces
scores = [85, 92, 78]
for i in range(len(scores) + 1): # +1 goes one index too far
print(scores[i])85
92
78
Traceback (most recent call last):
File "scores.py", line 4, in <module>
print(scores[i])
~~~~~~^^^
IndexError: list index out of rangeLoop over range(len(...)) — no +1 — or just iterate directly
scores = [85, 92, 78]
# Fix 1: drop the +1
for i in range(len(scores)):
print(scores[i])
# Fix 2: if you don't actually need the index, iterate directly
for score in scores:
print(score)Quick Reference
Frequently asked questions
What's the last valid index for a list of length n?
n - 1. A list of 3 elements has valid indices 0, 1, 2 — never 3. This is the single fact behind almost every IndexError: len(list) tells you the count of elements, not the last valid index, which is always one less.
Why doesn't Python just return None for an out-of-range index like some other operations do?
Silently returning a default value for a clearly invalid index would hide real bugs — you'd get wrong results instead of a clear error telling you exactly what went wrong. This is Python's general design philosophy of failing loudly rather than guessing at what you meant.
How does negative indexing interact with this error?
Negative indices count from the end — list[-1] is the last element, equivalent to list[len(list)-1]. The valid negative range is -len(list) to -1; anything more negative than -len(list) raises the same IndexError, for the same underlying reason.
Is enumerate() actually safer than a manual range(len(...)) loop?
Yes — enumerate(scores) can never produce an out-of-range index because it's generated directly from iterating the list itself, not from a separately computed range. It also reads more clearly when you need both the index and the value, which is the most common reason to reach for an index-based loop in the first place.
Watch your own loop's index, step by step
Paste your code into LearnBug and see exactly where the index steps out of bounds.