KeyError
d[key] on a key that is not in the dict. The message is the repr of the key — quotes included.
KeyError(*args)
Raised by
d[key], del d[key], d.pop(key), set.remove(x)
Message
repr(key) — KeyError: 'id', with the quotes
Quick fix
d.get(key, default) or key in d
Watch out
str(e) is quoted; use e.args[0] for the raw key
Demo
Live evaluation
Index a dict with a key. Keys are exact: case and type both matter.
Try:
Inputs
keystrkey to look up
Code
stock = {'apple': 3, 'pear': 0} stock['apple']
Result
3
Look at the quotes: the missing key 'plum' prints as KeyError: 'plum', and f'{e}' gives 'plum' in quotes too. KeyError's str() is the repr of the key — that is how an empty-string key still shows up as KeyError: '' instead of a blank message.
Constructor
| Name | Type | Required | Description |
|---|---|---|---|
| *args | object | no | Normally exactly one: the key that was not found. Stored in e.args. |
Attributes
| Attribute | Type | Meaning |
|---|---|---|
| args | tuple | The constructor arguments. args[0] is the missing key, unquoted — use it instead of str(e). |
| __context__ | BaseException | None | The exception being handled when this one was raised (implicit chaining). |
| __cause__ | BaseException | None | Set by raise ... from ...; e.g. a domain error raised from the KeyError. |
Common patterns
Default instead of exception
When a missing key is normal, ask for a default up front.
timeout = settings.get('timeout', 30)
EAFP lookup
Try the lookup and handle the miss — one hash lookup instead of two.
try: user = users[user_id] except KeyError: user = load_user(user_id)
Translate to a domain error
Hide the dict behind a meaningful error. from None drops the internal KeyError from the traceback; use from e to keep it as __cause__.
def get_plugin(name): try: return registry[name] except KeyError: raise ValueError(f'unknown plugin: {name}') from None
Examples
1. Missing key
{'a': 1}['b']
Returns
KeyError: 'b'2. str() is the repr of the key
str(KeyError('b'))
Returns
"'b'"3. e.args[0] is the raw key
try:
{'a': 1}['missing']
except KeyError as e:
key = e.args[0]
key
Returns
'missing'4. Caught as LookupError
try:
{}['x']
except LookupError as e:
print(type(e).__name__)
Returns
KeyError5. get() never raises
d = {}
d.get('x', 0)
Returns
06. defaultdict fills the gap
from collections import defaultdict
d = defaultdict(int)
d['x'] += 1
d
Returns
defaultdict(<class 'int'>, {'x': 1})7. set.remove raises it too
{1, 2}.remove(3)
Returns
KeyError: 3Pitfalls
1. str(e) adds quotes to the key
Building a message from str(e) gives doubled-up quoting. The raw key is e.args[0].
str(e)
try: {}['id'] except KeyError as e: msg = 'missing field ' + str(e) msg
"missing field 'id'"
e.args[0]
try: {}['id'] except KeyError as e: msg = 'missing field ' + e.args[0] msg
'missing field id'
2. A wide try block hides typos
except KeyError around a whole call also swallows KeyErrors caused by bugs inside it — here a misspelled key silently becomes the fallback.
Typo masked
config = {'port': 80} def port(): return config['prot'] # typo try: p = port() except KeyError: p = 8080 p
8080
Narrow lookup
config = {'port': 80} p = config.get('port', 8080) p
80
3. Lists raise IndexError, not KeyError
Code that handles "missing entry" for both dicts and lists must catch the common base class.
except KeyError
try: r = [1, 2][5] except KeyError: r = 'handled' r
IndexError: list index out of range
except LookupError
try: r = [1, 2][5] except LookupError: r = 'handled' r
'handled'
When to use
Use it
- The key is required, so a miss is a real error (mandatory config field)
- EAFP style: try the lookup, handle the miss once
- Raising it from your own Mapping subclass for a missing key
Reach for something else
- A missing key is normal → d.get(key, default)
- Counting or grouping → collections.defaultdict / Counter
- Only checking presence → key in d
Notes
CPython impl
Objects/exceptions.c — KeyError_str returns repr(args[0]) when there is exactly one argument
Catch via
except LookupError catches both KeyError and IndexError
dict hook
A dict subclass can define __missing__(key) to return a value instead of raising
FAQ
KeyError overrides __str__: with one argument it returns repr(key), not str(key). So a missing 'id' prints as KeyError: 'id', and a missing empty-string key prints as KeyError: '' rather than an empty message.