Python KeyError vs IndexError Explained

In Python, KeyError and IndexError are both lookup exceptions raised when attempting to access an item that cannot be found within a container. The primary distinction lies in the underlying data structure: an IndexError occurs when querying an out-of-range integer position in an ordered sequence like a list or tuple, whereas a KeyError occurs when searching for a non-existent key in a mapping structure like a dictionary. Understanding the design of the data structure you are using determines which exception Python will raise.

When Python Raises an IndexError

An IndexError is raised by sequence types—collections where elements are ordered sequentially by integer offsets starting at zero. These types include list, tuple, str, bytes, and range.

An IndexError occurs in the following scenarios:

1. Indexing Beyond the Bounds of a Sequence

Attempting to access an index equal to or greater than the sequence length, or a negative index whose absolute value exceeds the sequence length, will raise an IndexError.

fruits = ["apple", "banana", "cherry"]

# Valid indices: 0, 1, 2 (or -3, -2, -1)
print(fruits[5])    # IndexError: list index out of range
print(fruits[-10])  # IndexError: list index out of range

2. Accessing Items in an Empty Sequence

Attempting to retrieve any element by index from an empty sequence immediately raises an IndexError.

empty_list = []
first_item = empty_list[0]  # IndexError: list index out of range

3. Removing Items with pop() at an Invalid Index

Using .pop() on a list with an out-of-bounds index, or calling .pop() on an empty list, raises an IndexError.

items = [1, 2]
items.pop(5)  # IndexError: pop index out of range

Note: Sequence slicing (e.g., fruits[1:10]) does not raise an IndexError; it simply returns as many items as are available within the specified range.


When Python Raises a KeyError

A KeyError is raised by mapping types—structures that associate unique, hashable keys with values. The most common mapping type in Python is dict. A KeyError is also raised by set when attempting to remove an element that does not exist.

A KeyError occurs in the following scenarios:

1. Accessing a Non-Existent Dictionary Key

When using square-bracket notation to look up a key that is not in the dictionary, Python raises a KeyError.

user = {"name": "Alice", "age": 30}
print(user["email"])  # KeyError: 'email'

2. Deleting a Non-Existent Key

Using the del keyword or .pop() on a dictionary key that does not exist triggers a KeyError.

user = {"name": "Alice"}
del user["email"]      # KeyError: 'email'
user.pop("email")      # KeyError: 'email'

3. Removing a Missing Element from a Set

Calling .remove() on a set with a value not present in that set raises a KeyError (unlike .discard(), which fails silently).

tags = {"python", "coding"}
tags.remove("javascript")  # KeyError: 'javascript'

The Integer Key Ambiguity

A common source of confusion occurs when a dictionary uses integers as keys. Even if the lookup value is an integer that looks like a sequential index, the container remains a mapping, so a failed lookup will always raise a KeyError, never an IndexError.

data = {0: "first", 1: "second"}

# Looking for key 5, not the 5th element:
print(data[5])  # KeyError: 5

Comparison Summary

Feature IndexError KeyError
Applicable Types Sequences (list, tuple, str, bytes) Mappings (dict) and Sets (set.remove())
Lookup Mechanism Positional integer offset (0 to n - 1) Any hashable key (strings, ints, tuples, etc.)
Trigger Cause Position is outside the container boundaries Key does not exist in the collection

How to Prevent Both Exceptions

Preventing IndexError:

Preventing KeyError: