Quick answer
A KeyError means you read a dictionary key that doesn't exist with square brackets (d['name']). The key after the error is the missing one. Use d.get('name') (or d.get('name', default)) for keys that may be absent, and print d.keys() to check for a typo, wrong case, or stray whitespace.
The exact error string
user = {"name": "Ada", "age": 36}
print(user["email"])
Traceback (most recent call last):
File "app.py", line 2, in <module>
print(user["email"])
KeyError: 'email'
The dict is valid; it simply has no 'email' key. Square-bracket access demands the key exist — when it doesn't, Python raises KeyError and prints the key it couldn't find.
Why Python raises KeyError
Square-bracket access (d[key]) treats the key as required. Python raises KeyError instead of quietly returning None so that missing required data is detected immediately, at the point of the bug, rather than failing mysteriously later. dict.get() is the explicit opt-in for keys that may legitimately be absent — so the two access styles document your intent.
Common KeyError variations
The cause is the same whatever key is named after the error — these are just different missing keys:
KeyError: 'name',KeyError: 'email',KeyError: 'id'— a string field missing from a dict (often an API field that wasn't sent)KeyError: 'user',KeyError: 'data',KeyError: 'response'— a wrapper key that isn't where you expect; the payload is nested differentlyKeyError: 0— integer access on a dict with no key0(usually the value is really a list — see Fix 4)
Fix 1: use .get() for optional keys
dict.get() returns None (or a default you choose) instead of raising, which is the right tool whenever a key may legitimately be missing:
user.get("email") # None — no error
user.get("email", "n/a") # 'n/a' — your own default
age = user.get("age", 0) # 0 if 'age' is missing
print(age + 1) # safe arithmetic with a fallback
The most common cause: optional fields in API responses
APIs routinely omit optional fields, so a key present on one response is missing on the next — and square-bracket access then raises. Use .get() for anything not guaranteed:
user = response.json()
email = user.get("email") # ✅ None if the field was omitted
email = user["email"] # ❌ KeyError when 'email' is absent
Fix 2: the key isn't what you think
If a key "should" be there but isn't, print the real keys. The usual culprits are casing, whitespace, and string-vs-int:
print(list(user.keys())) # ['name', 'age'] -> no 'email'
# common mismatches:
data["Name"] # ❌ case differs from 'name'
data["name "] # ❌ trailing space in the key
data[1] # ❌ key is the string '1', not int 1
Fix 3: nested JSON without crashing
For nested structures, chain .get() with empty-dict defaults so a missing level returns nothing instead of raising:
city = data.get("user", {}).get("address", {}).get("city")
# or be explicit when a missing key is truly exceptional:
try:
city = data["user"]["address"]["city"]
except KeyError as e:
print(f"missing key: {e}")
city = None
When the JSON shape varies or is deeply nested, a JSONPath query navigates it without a chain of .get() calls — and the JSON Formatter shows the exact keys the API actually returned.
Fix 4: KeyError: 0 usually means a list
KeyError: 0 means you used d[0] on a dict with no key 0. Often the value is really a list, where [0] is the first element:
resp = {"items": [{"id": 1}, {"id": 2}]}
resp[0] # ❌ KeyError: 0 — resp is a dict
resp["items"][0] # ✅ {'id': 1} — index the list inside
Debugging checklist
- ✓ Read the missing key printed after
KeyError: - ✓ Optional key? Use
d.get(key)ord.get(key, default) - ✓ Print
list(d.keys())— check case, whitespace, and string-vs-int - ✓ Nested? Chain
.get(..., {})or wrap intry/except KeyError - ✓
KeyError: 0? The value is probably a list — index it by position - ✓ From an API? Confirm the real shape in the JSON Formatter
Frequently Asked Questions
What does KeyError mean in Python?
KeyError means you tried to read a dictionary key that does not exist, like d['name'] when there is no 'name' key. Python shows the missing key after the error. It is raised by square-bracket access; the dict itself is fine, the key just isn't in it.
How do I avoid KeyError for an optional key?
Use dict.get(): d.get('name') returns None instead of raising when the key is missing, and d.get('name', 'default') lets you supply a fallback. Use .get() whenever a key may legitimately be absent.
Why does the key look like it exists but still fails?
Common reasons: a different case ('Name' vs 'name'), trailing or leading whitespace in the key, an integer key vs a string key (1 vs '1'), or the data shape differs from what you expect. Print d.keys() to see the exact keys, including casing and spaces.
How do I handle nested JSON without KeyError?
Chain .get() with defaults so a missing level returns an empty dict instead of raising: data.get('user', {}).get('address', {}).get('city'). For deep or variable structures, wrap the access in try/except KeyError, or use a JSONPath query to navigate safely.
What causes KeyError: 0?
KeyError: 0 means you used d[0] on a dict that has no key 0. Often the value is actually a list (where [0] is the first element) — or it is a dict keyed by strings like '0'. Check the type: a list uses integer positions, a dict uses its actual keys.
Should I use try/except or .get()?
Use .get() when a missing key is normal and you want a default. Use try/except KeyError when a missing key is genuinely exceptional and you want to handle or log it. Avoid checking if key in d and then indexing again — .get() does it in one lookup.
Find the keys your JSON actually has
Paste the payload into the formatter to see every key and its nesting, or test a path with the JSONPath tester — nothing is uploaded to a server.