KeyError

d[key] on a key that is not in the dict. The message is the repr of the key — quotes included.

InheritsBaseException›Exception›LookupError›KeyError
Lookup exceptionPython 3 (all)Live demo
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

NameTypeRequiredDescription
*argsobjectnoNormally exactly one: the key that was not found. Stored in e.args.

Attributes

AttributeTypeMeaning
argstupleThe constructor arguments. args[0] is the missing key, unquoted — use it instead of str(e).
__context__BaseException | NoneThe exception being handled when this one was raised (implicit chaining).
__cause__BaseException | NoneSet 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
KeyError
5. get() never raises
d = {} d.get('x', 0)
Returns
0
6. 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: 3

Pitfalls

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.