Dictionaries produce a small, predictable set of TypeErrors, and each message points at a specific mistake. This guide covers the five you will actually meet: unhashable keys, subscripting something that is not a dictionary, calling a dictionary like a function, the dict() constructor being given the wrong shape, and trying to merge with +.
1. unhashable type: ‘list’#
data = {}
data[["a", "b"]] = 1
TypeError: unhashable type: 'list'
Dictionary keys must be hashable, which in practice means they must not be able to change. A list can change, so its hash would change, and the dictionary would lose track of where it stored the value.
Use a tuple instead:
data[("a", "b")] = 1 # tuples are immutable
data[frozenset({"a", "b"})] = 2 # when order should not matter
The same error appears when you put a list into a set, or use one as a key indirectly:
seen = set()
seen.add([1, 2]) # TypeError
seen.add((1, 2)) # fine
A tuple only counts as hashable if everything inside it is:
key = (1, [2, 3])
hash(key) # TypeError: unhashable type: 'list'
2. ‘NoneType’ object is not subscriptable#
def load_config():
print("loading") # forgot to return
config = load_config()
print(config["host"])
TypeError: 'NoneType' object is not subscriptable
“Subscriptable” means “can be indexed with square brackets”. None cannot. The real bug is that something gave you None where you expected a dictionary — a function with a missing return, a failed lookup, or a JSON field that was absent.
config = load_config()
if config is None:
raise ValueError("Configuration could not be loaded")
print(config["host"])
The variation 'dict' object is not subscriptable does not exist — but 'method' object is not subscriptable does, and it means you forgot brackets:
print(person.keys[0]) # TypeError
print(list(person.keys())[0]) # correct
3. ‘dict’ object is not callable#
person = {"name": "Ana"}
print(person("name"))
TypeError: 'dict' object is not callable
Round brackets call something; square brackets look something up. Dictionaries use square brackets.
There is a nastier version of this. If you name a variable dict, you replace the built-in:
dict = {"a": 1} # shadows the built-in type
other = dict() # TypeError: 'dict' object is not callable
The same applies to list, set, str, id and type. Name the variable something descriptive and the problem disappears.
4. dict expected at most 1 argument#
d = dict("a", 1)
TypeError: dict expected at most 1 argument, got 2
The constructor takes keyword arguments, a mapping, or an iterable of pairs — not two loose values:
d = {"a": 1}
d = dict(a=1)
d = dict([("a", 1), ("b", 2)])
d = dict(zip(["a", "b"], [1, 2]))
Note that dict(a=1) only works when the key is a valid Python identifier. For a key like "first name" you need the literal form or the pairs form.
5. unsupported operand type(s) for +: ‘dict’ and ‘dict’#
merged = {"a": 1} + {"b": 2}
TypeError: unsupported operand type(s) for +: 'dict' and 'dict'
Three ways to merge, all correct:
a = {"x": 1, "y": 2}
b = {"y": 99, "z": 3}
merged = {**a, **b} # any Python 3.5+
merged = a | b # Python 3.9+
merged = dict(a); merged.update(b) # in place
In all three, later values win: merged["y"] is 99. That is what makes this the standard pattern for layering defaults under user settings:
settings = {**DEFAULTS, **user_settings}
Reading any TypeError in three steps#
- Read the type names in the message. “NoneType” tells you something returned nothing; “list” in an unhashable message tells you which value is the problem.
- Look at the last line of the traceback that is in your own file. Library frames below it are usually consequences, not causes.
- Print the type, not the value.
print(type(config), config)settles it immediately, and often the value alone looks fine.
Bonus: KeyError is not a TypeError#
person = {"name": "Ana"}
print(person["email"]) # KeyError: 'email'
KeyError means the dictionary is fine but the key is not in it. Use .get() with a default, or check with in first — and only when a missing key is genuinely acceptable.
Questions people ask#
Why must dictionary keys be immutable?
The dictionary stores each entry in a slot chosen by the key’s hash. If the key changed afterwards, its hash would point somewhere else and the value would be unreachable. Forbidding mutable keys prevents that entirely.
Can I use an object as a key?
Yes, if it is hashable. Custom classes are hashable by default, using identity. If you define __eq__ you must also define __hash__, or instances become unhashable.
What is the difference between TypeError and ValueError?
TypeError means the kind of thing is wrong — a list where a string was needed. ValueError means the type is right but the content is not, such as int("abc").
How do I merge dictionaries deeply?
Neither | nor {**a, **b} merges nested dictionaries — a nested value in the second simply replaces the first. For deep merging you need a small recursive function, or a library built for it.
Where to go next#
- Python dictionaries explained — the full picture behind these errors.
- Dictionary key checking with in — avoiding KeyError cleanly.
- Why is my Python code not working? — reading tracebacks in general.