Your script crashes, and the last line looks like this:
KeyError: 'banana'
It means you asked a dictionary for a key it doesn't have. The text after the colon is the key Python couldn't find. That's your best clue. Below are the six situations that cause this in real programs, the exact message each one gives, and how to fix it. I ran every example on Python 3.11, 3.12 and 3.14. All three print the same error.
What the Error Means
A dictionary maps keys to values. When you write d[key], Python looks the key up. If it's missing, Python raises KeyError instead of quietly handing you something. That's deliberate. A wrong lookup is usually a bug, and Python wants you to see it. Also, the message shows the key exactly as Python sees it. KeyError: 'name' has quotes, so the key is a string. KeyError: 1 has none, so the key is the integer 1. That one detail solves cause 3 below by itself.
How to Find the Cause Fast
Start with the line the traceback points to. Print the key you used next to the dictionary's keys. Wrap them in repr() so hidden spaces and quote marks show up:
print(repr(key), list(d.keys()))
If your key isn't in that list, one of the six causes below is the reason.
The Six Causes
1. The Key Really Is Not There
prices = {"apple": 1.20, "pear": 0.90}
print(prices["banana"]) # KeyError: 'banana'
Simple case. You looked up something that was never stored. Sometimes a missing key is normal for your data. Then don't catch the error. Ask the dictionary a different way. d.get(key, default) gives you the default when the key is missing, and key in d checks first:
prices = {"apple": 1.20, "pear": 0.90}
print(prices.get("banana", "not for sale")) # default instead of an error
if "banana" in prices: # or test first
print(prices["banana"])
else:
print("not for sale")
2. A Typo or the Wrong Capital Letter
user = {"Name": "Ana", "email": "ana@example.com"}
print(user["name"]) # KeyError: 'name' (the key is "Name")
Keys are case sensitive. "name" and "Name" are two different keys. So are "user_id" and "userId", a mismatch you'll often hit when the data comes from a JavaScript API. Type the exact spelling. Or clean up the keys once, right when you load the data:
user = {"Name": "Ana", "email": "ana@example.com"}
print(user["Name"]) # use the exact key
# if the case is unreliable, normalise the keys once:
clean = {k.lower(): v for k, v in user.items()}
print(clean["name"])
3. The Key Is a Number but Stored as a String (or the Other Way Round)
import json
data = json.loads('{"1": "one", "2": "two"}') # JSON object keys are always strings
print(data[1]) # KeyError: 1
The key 1 and the key "1" aren't the same thing. JSON trips people up here a lot. JSON object keys are always strings, so after json.loads you get "1", even if the original Python dictionary used 1. The message tells you: KeyError: 1 has no quotes. Use the string key, or convert the keys once:
import json
data = json.loads('{"1": "one", "2": "two"}')
print(data["1"]) # use the string key
# or convert the keys once after loading:
numbered = {int(k): v for k, v in data.items()}
print(numbered[1])
4. pop() or del on a Key That Is Missing
cart = {"milk": 2}
cart.pop("bread") # KeyError: 'bread'
Removing a key that isn't there fails just like reading one. pop accepts a second argument and returns it instead of raising, so pop(key, None) is the safe form. The del statement has no default. Check with in first:
cart = {"milk": 2}
cart.pop("bread", None) # pop with a default never raises
print(cart)
if "bread" in cart:
del cart["bread"] # del has no default, so test first
5. A Missing Key in Nested Data
order = {"id": 7, "customer": {"name": "Li"}}
print(order["customer"]["address"]["city"]) # KeyError: 'address'
JSON from an API can be missing a key at any level. The error only names the key that failed ('address' here). It doesn't show the path. Chain get() calls and use an empty dictionary as the default. That way every step still has something to look inside:
order = {"id": 7, "customer": {"name": "Li"}}
city = order.get("customer", {}).get("address", {}).get("city", "unknown")
print(city)
6. Hidden Whitespace in a CSV Header
import csv
import io
text = "name, age\nAna, 31\n" # note the space after the comma
for row in csv.DictReader(io.StringIO(text)):
print(row["age"]) # KeyError: 'age' (the key is " age")
Look at the header: name, age. There's a space after the comma, so csv.DictReader builds the key " age" with a leading space. The error says KeyError: 'age', but the dictionary holds ' age'. That's where repr() earns its keep. Tell the reader to skip that space, or strip the headers yourself:
import csv
import io
text = "name, age\nAna, 31\n"
for row in csv.DictReader(io.StringIO(text), skipinitialspace=True):
print(row["age"])
Other Ways to Avoid KeyError
defaultdict. If you're counting or grouping, collections.defaultdict creates the missing entry for you. So counts[word] += 1 works even the first time. setdefault. d.setdefault(key, []) hands back the existing value, or stores a new one and returns that. try/except. Use except KeyError: when a missing key is rare and you can recover. When it's common, get() or in reads better and runs faster than catching the error over and over.
One more thing. KeyError isn't only about dictionaries. os.environ["NAME"] raises it when the environment variable isn't set. Pandas raises it when a column name is wrong. The advice is the same: print the keys you really have and compare.
Should You Catch KeyError?
Sometimes. Just watch how much code you put inside the try. Wrap ten lines in one try/except KeyError and you can't tell which lookup failed. You may even hide a real bug. Keep the try around the one lookup, or use get() and skip the exception entirely:
settings = {"theme": "dark"}
try:
size = settings["font_size"]
except KeyError:
size = 14 # a sensible default
# the same thing, shorter:
size = settings.get("font_size", 14)
Here's another good habit. When a required key is missing, raise a clearer error yourself, like raise ValueError("config needs a 'font_size' entry"). A message that names the missing setting saves the next reader from digging through a traceback. That reader is often you in three months.
Quick Reference
| Situation | Fix |
|---|---|
| Key not stored, and that is normal |
d.get(key, default) or key in d
|
| Typo or wrong case | Use the exact key, or lower-case the keys on load |
KeyError: 1 after loading JSON |
Use the string "1", or convert the keys to int |
| Removing a missing key |
d.pop(key, None), or test before del
|
| Missing key in nested data | Chain get(key, {}) calls |
| Hidden space in a CSV header |
skipinitialspace=True, or strip the headers |
| Counting or grouping | defaultdict |
The Rule, Stated Once
The key you asked for isn't in the dictionary. That's all KeyError says. The message shows the exact key, quotes included, so compare it with repr() of the keys you actually have. Then decide. If a missing key is a bug, let it fail. If it's normal, use get(), a default, or in.
Want more Python errors with a clear cause? Try IndexError: List Index Out of Range, TypeError: NoneType Is Not Subscriptable, IndentationError and UnboundLocalError. New to Python? Start with Python for Beginners.
Download the code: the broken and fixed versions of all six causes are on GitHub in Python-projects/Troubleshooting/keyerror. Run any file with python bug.py from inside its folder.
Top comments (0)