Index and Key Errors
IndexError reports a sequence position outside its valid range, while KeyError reports a missing dictionary or set key. You will trace calculated and nested indexes, inspect exact keys, and choose between required lookups, membership checks, and meaningful defaults.
IndexError and KeyError both report that a lookup found no matching item.
They apply to different lookup systems.
A list uses integer positions. The list has three items at indexes 0, 1,
and 2, so position 3 raises:
IndexError: list index out of range
A dictionary uses keys rather than positions:
The key "Grace" is absent, so Python raises:
KeyError: 'Grace'
The first diagnostic question is therefore: was the program looking for a sequence position or a dictionary key?
Check an Index Against the Sequence Length
For a sequence of length n, valid positive indexes begin at 0 and end at
n - 1.
items = ["a", "b", "c"]
The relationship is:
| Property | Value |
|---|---|
len(items) | 3 |
| valid positive indexes | 0, 1, 2 |
| final positive index | len(items) - 1, or 2 |
The length is a count, not an index. items[len(items)] is one position beyond
the final item.
Negative indexes count backward from the end:
| Index | Selected item |
|---|---|
-1 | "c" |
-2 | "b" |
-3 | "a" |
For this length-three list, -3 through 2 are valid. Both 3 and -4 raise
IndexError.
An empty sequence has length zero and no valid index:
Before reading a required first item, decide what an empty sequence should mean for the program. It may require a separate result:
Loop Boundaries Commonly Produce Invalid Indexes
This loop goes one step too far:
range(0, len(items) + 1) produces 0, 1, 2, and 3. The first three
lookups succeed, which can make the eventual failure seem less obvious. The
last lookup requests items[3].
When the index itself is required, stop at len(items):
The stop value of range() is excluded, so this produces only 0, 1, and
2.
When the index is not needed, iterate over the items directly:
This form expresses the real task and removes the manual boundary entirely.
Find where the boundary is crossed
Run the program and inspect the last index printed before the error. Then remove the extra one from the range stop.
Ready to run.
The temporary output identifies the requested index. Compare it with the sequence length and valid range rather than changing the list blindly.
Trace Where a Calculated Index Came From
The failing lookup may use an index calculated earlier:
The traceback points to scores[position], but the faulty boundary decision
appears in position = len(scores). Inspect both:
As with a traceback, separate where the failure appears from where the invalid value originated.
Nested lookups require checking one level at a time:
grid[1] succeeds and produces [6, 8]. The second lookup then requests index
2 from that two-item row, whose valid indexes are 0 and 1. Naming the
intermediate value makes the failing level visible:
Slices Clamp Their Boundaries
A single-item lookup requires an existing position:
items[10]
A slice can extend beyond the sequence without raising IndexError:
items[0:10]
Python returns the available part of the sequence. For ["a", "b", "c"], the
result is still ["a", "b", "c"].
This behavior does not prove that the boundary is correct. If the program expected ten items but received three, the shorter slice may reveal an earlier data problem. Do not replace an invalid single index with a slice merely to silence an error.
A Dictionary Key Must Match Exactly
Dictionary keys are values, not position numbers:
scores = {"Ada": 8, "Lin": 9}
These lookups ask different questions:
Only "Ada" is present. Dictionaries do not assign an automatic position key
of 0, and string keys remain case-sensitive.
When a key is stored in a name, inspect both the requested key and the available keys:
The first two lines provide evidence before the failing lookup.
Choose Whether a Missing Key Is an Error
Square-bracket lookup is appropriate when a key is required:
score = scores["Ada"]
If the key is missing, KeyError exposes incomplete or misspelled data.
When absence is an expected possibility, test membership:
Use .get() when a missing key has a meaningful fallback:
Here, zero accurately means that no attempts have been recorded. A fallback of zero would be misleading for a required exam score, because a missing record and an actual score of zero mean different things.
Calling .get(key) without a fallback returns None for a missing key. If the
dictionary can also store None, use key in mapping when the program must
distinguish “missing” from “present with the value None.”
A set can also raise KeyError. Removing an absent set item with .remove()
raises the error, while .discard() leaves the set unchanged. Choose
.remove() when absence indicates a mistake and .discard() when absence is
an acceptable state.
A list has length 5. What is its final valid positive index?
Compute it first, then check your number.
HintSeparate count from position
A length-five list has five positions beginning with zero.
SolutionThe final index is four
The valid positive indexes are 0, 1, 2, 3, and 4. Therefore the
final valid positive index is 5 - 1, or 4.
Change the range so the program prints all three items without an error.
Stop before the sequence length
Ready to run.
HintUse the excluded stop value
range(len(items)) stops before the length.
SolutionRemove the extra one
Write for index in range(len(items)):. The range produces 0, 1, and
2, matching the list's valid positive indexes.
Which part of this expression raises IndexError?
Select one choice, then check.
HintName the intermediate row
First evaluate row = grid[1], then consider row[2].
SolutionThe column lookup fails
grid[1] produces [6, 8]. That row has length 2 and valid indexes 0
and 1, so its lookup at index 2 raises IndexError.
A model configuration must contain "learning_rate". Which lookup best keeps
a missing value visible as an error?
Select one choice, then check.
HintDo not hide incomplete configuration
The program should stop if the required key is absent.
SolutionUse square brackets for required data
config["learning_rate"] returns the stored value or raises KeyError.
Either outcome is more accurate than silently treating missing configuration
as zero or None.
Complete the program so a missing student has zero recorded attempts.
Retrieve an expected default
Ready to run.
HintAbsence has a defined meaning here
Use attempts.get(student, 0).
SolutionUse get() with zero as the fallback
Write count = attempts.get(student, 0). Since "Grace" is absent, the
method returns 0. It does not insert "Grace" into the dictionary.
Inspect the Lookup and Its Source
When Python reports IndexError or KeyError:
- identify whether the lookup uses a sequence position or a key;
- print the requested index or key before the failing operation;
- for an index, compare it with the sequence length and valid boundaries;
- for nested indexing, evaluate and name one level at a time;
- for a key, compare its exact value and type with the available keys;
- decide whether absence indicates a bug or an expected case with a meaningful alternative.
Do not add a fallback only to make the traceback disappear. A safe lookup must
preserve the program's intended meaning. The next lesson introduces narrow
try and except blocks for failures that are expected at a program boundary.