KeyError: 'key' in Python
- tool
- Python
- exit code
- 1
- versions
- Python 3.x
- os
- any
KeyError: 'b' means the code read d["b"] and the dictionary d has no key 'b'. The text after KeyError: is the repr() of the missing key. If a missing key is a normal case, use d.get("b", default). If it is a bug, compare the key you look up with d.keys().
Reproduce it
cat > app.py <<'EOF'
d = {"a": 1}
print(d["b"])
EOF
python app.pyOutput · exit 1
Traceback (most recent call last):
File "/tmp/work/app.py", line 2, in <module>
print(d["b"])
~^^^^^
KeyError: 'b'Fixes
cat > fix.py <<'EOF'
d = {"a": 1, 1: "int key"}
print(d.get("b", 0)) # default instead of an error
print("b" in d) # membership test
print(d["1"] if "1" in d else "'1' is not 1") # str and int keys differ
try:
d["b"]
except KeyError as e:
print("KeyError for", repr(e.args[0]))
EOF
python fix.pyOutput
0 False '1' is not 1 KeyError for 'b'
Count or group without KeyError
collections.defaultdict creates the missing entry on first access.
cat > count.py <<'EOF'
from collections import defaultdict
counts = defaultdict(int)
for word in "a b a".split():
counts[word] += 1
print(dict(counts))
EOF
python count.pyOutput
{'a': 2, 'b': 1}Keys from JSON and the environment
JSON object keys are always strings, so a dictionary loaded with json.loads has "1", not 1, and environment variables live in os.environ, which raises KeyError for an unset name:
cat > sources.py <<'EOF'
import json, os
d = json.loads('{"1": "one"}')
print(list(d)) # keys are strings
print(d.get(1), d.get("1")) # int lookup misses, str lookup hits
print(os.environ.get("APP_MODE_NOT_SET", "development"))
EOF
python sources.pyOutput
['1'] None one development
How to recognise the cause
| Clue | Likely cause |
|---|---|
| The key in the message differs from the stored one by case, spaces or a typo | A misspelled key; compare repr(key) with list(d) |
KeyError: 1 while the data shows "1" | Integer lookup in a dictionary whose keys are strings, often after JSON |
| The key exists for some records but not others | Optional fields in API or CSV data; use get() with a default |
The traceback ends inside os.py with __getitem__ | An unset environment variable read with os.environ[...] |
| The key is a column name and the object is a pandas DataFrame | The column does not exist; check df.columns for spaces and case |
On Python 3.11 and later the carets under the failing line point at the exact subscript, which helps with nested lookups such as data["user"]["email"].
Prevent it
Validate the shape of external data once, where it enters the program, instead of indexing deep into it everywhere. Use d.get() only for keys that are truly optional, setdefault() or defaultdict when you build up a dictionary, and keep d[key] for keys that must exist so a missing one fails loudly at the right place.
FAQ
Why does the message show KeyError: 1 without quotes?
The text after KeyError: is the repr() of the missing key. A string key prints with quotes, an integer key without them, which is a quick way to spot a str versus int mix-up.
Should I use d.get() everywhere to avoid KeyError?
No. Use get() when a missing key is a normal case and the default makes sense. When the key must exist, d[key] is better: the KeyError points at the bug instead of letting a None travel further and fail later.
Why does os.environ raise KeyError?
os.environ behaves like a dictionary, so reading an unset variable with os.environ['NAME'] raises KeyError: 'NAME'. Use os.environ.get('NAME', default) or os.getenv('NAME') when the variable is optional.
Sources
- docs.python.orgdocs.python.org/3/library/exceptions.html#KeyError
- docs.python.orgdocs.python.org/3/library/stdtypes.html#dict.get
- docs.python.orgdocs.python.org/3/library/collections.html#collections.defaultdict
- docs.python.orgdocs.python.org/3/library/os.html#os.environ
- docs.python.orgdocs.python.org/3/library/json.html#json-to-py-table