KeyError: 'X'
1 · The lesson
readWhat this error means
A KeyError is raised when you try to access a key in a dictionary (or a dict-like object) that does not exist. Python is telling you: "I looked for that key, it's not here." It is the dictionary equivalent of IndexError for lists.
When you see it
>>> user = {"name": "Grace", "age": 28}
>>> user["email"]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'email'Why it happens
Square-bracket access on a dict (d[k]) demands that k is already present. If it isn't, Python refuses to invent a value and raises KeyError. This often happens after parsing JSON, reading config files, or processing user input where a field is optional or misspelt.
How to fix it
1. Use .get() when the key may be missing (returns None by default):
email = user.get("email") if email is None: print("no email on file")
setup added so this can run · defines user
# Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) user = _AutoMock('user')
2. Use .get(key, default) to supply a fallback:
email = user.get("email", "unknown@example.com")
setup added so this can run · defines user
# Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) user = _AutoMock('user')
3. Check membership explicitly with in:
if "email" in user: send_to(user["email"])
setup added so this can run · defines user, send_to
# Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) user = _AutoMock('user') def send_to(*_a, **_kw): print('-> send_to() called') return _AutoMock('send_to()')
4. Use setdefault to initialise a key if absent:
counts = {}
for word in words:
counts.setdefault(word, 0)
counts[word] += 1 setup added so this can run · defines words
words = ["alpha", "beta", "gamma"]
For counting specifically, collections.defaultdict(int) or collections.Counter is cleaner.
When you'd actually see this in real code
- Parsing JSON from an API where a field is optional:
data["address"]["postcode"]blows up if the user has no address. - Dict comprehensions filtering data:
{k: src[k] for k in keys}raisesKeyErrorthe moment one ofkeysis missing fromsrc. Use{k: src.get(k) for k in keys}or filterkeysfirst. - Config lookups where an environment variable wasn't set: prefer
os.environ.get("API_KEY")overos.environ["API_KEY"]unless you genuinely want to crash on missing config.
Related errors
See Also
- All Python errors — the full index, by type and by when it happens.
- Dictionary cheat sheet
- Working with dicts tutorial
Practice this
on practicepython.inShort exercises that run in your browser and tell you what your code actually did, not just whether a test passed.