A KeyError occurs in Python when you try to access a dictionary key that does not exist. Unlike some languages that return null for missing keys, Python raises an exception to alert you that the key was not found. This helps prevent silent bugs where missing data goes unnoticed.
# ❌ Problem
user = {"name": "Alice", "email": "alice@example.com"}
print(user["username"]) # KeyError: 'username'
# ✅ Solution 1: Use .get() with a default value
print(user.get("username", "N/A")) # "N/A"
# ✅ Solution 2: Check key existence first
if "username" in user:
print(user["username"])
The most straightforward case "” you try to access a key that was never added to the dictionary. Use .get() to safely retrieve values with a fallback default.
Dictionary keys are case-sensitive strings. "Username" and "username" are different keys. Always double-check the exact key names, especially when working with API responses or JSON data.
If a key is removed using del or dict.pop() and then accessed again, Python raises a KeyError. Use pop(key, default) to safely remove keys without raising an error.
When working with nested dictionaries (common with JSON API responses), accessing a nested key without checking parent keys first can raise a KeyError at any level of nesting.
config = {"host": "localhost", "port": 5432}
db_name = config["database"] # KeyError: 'database'
config = {"host": "localhost", "port": 5432}
# ✅ Option 1: .get() with default
db_name = config.get("database", "mydb") # "mydb"
# ✅ Option 2: setdefault() "” adds key if missing
config.setdefault("database", "mydb")
print(config["database"]) # "mydb"
# ✅ Option 3: Use defaultdict
from collections import defaultdict
config = defaultdict(str, {"host": "localhost"})
print(config["database"]) # "" (empty string default)
response = {"userId": 1, "userName": "Alice"}
print(response["user_id"]) # KeyError: 'user_id' (camelCase vs snake_case)
print(response["username"]) # KeyError: 'username' (wrong case)
response = {"userId": 1, "userName": "Alice"}
print(response["userId"]) # ✅ 1
print(response["userName"]) # ✅ "Alice"
# Debug: print all keys to see exact names
print(response.keys()) # dict_keys(['userId', 'userName'])
session = {"user_id": 42, "token": "abc123"}
del session["token"]
print(session["token"]) # KeyError: 'token'
session = {"user_id": 42, "token": "abc123"}
# ✅ Safe removal with pop() and a default
token = session.pop("token", None)
# ✅ Check before accessing
if "token" in session:
print(session["token"])
else:
print("Token not found")
data = {"user": {"name": "Alice"}}
city = data["user"]["address"]["city"] # KeyError: 'address'
data = {"user": {"name": "Alice"}}
# ✅ Chain .get() calls
city = data.get("user", {}).get("address", {}).get("city", "Unknown")
print(city) # "Unknown"
# ✅ Or use try/except for complex nesting
try:
city = data["user"]["address"]["city"]
except KeyError:
city = "Unknown"
dict[key] raises a KeyError if the key does not exist. dict.get(key) returns None (or a specified default) if the key is missing, making it safer for optional keys.
Use .get(key, default) for a default value, use "key" in dict to check existence, use try/except KeyError to handle the error, or use collections.defaultdict for automatic defaults.
Chain .get() calls: data.get("user", {}).get("address", {}).get("city", "Unknown"). This returns the default at any level where a key is missing.
defaultdict from the collections module creates missing keys automatically using a factory function. Use it when you want all missing keys to have the same default type, like defaultdict(list) for grouping.
Yes. Calling set.remove(element) raises a KeyError if the element is not in the set. Use set.discard(element) instead, which silently does nothing if the element is absent.
Explore 500+ free tutorials across 20+ languages and frameworks.