TypeError: unhashable type: 'set' (tested fix)

TypeError: unhashable type: 'set' means a set was used as a set member or a dict key. Sets are mutable, so they cannot be hashed. We reproduced each case on Python 3.13 and tested the fix, frozenset.

1. Cause: a set inside a set, or a set as a dict key

groups = [{"a", "b"}, {"b", "a"}, {"c"}]
for label, action in [("set of sets", lambda: set(groups)),
                      ("dict key", lambda: {groups[0]: 1}),
                      ("set.add", lambda: set().add(groups[0]))]:
    try:
        action()
    except TypeError as e:
        print(f"{label:12} TypeError: {e}")

Output:

set of sets  TypeError: unhashable type: 'set'
dict key     TypeError: unhashable type: 'set'
set.add      TypeError: unhashable type: 'set'

Set members and dict keys must be hashable, and a set is mutable, so it has no hash. Typical triggers: deduplicating a list of sets (tags, permissions, graph edges), or counting pairs with a set as the dict key.

2. Fix: use frozenset

# Fix: frozenset is the immutable, hashable set
groups = [{"a", "b"}, {"b", "a"}, {"c"}]
unique = {frozenset(g) for g in groups}
print(len(unique), sorted(sorted(g) for g in unique))
pair_count = {frozenset({"alice", "bob"}): 3}
print(pair_count[frozenset({"bob", "alice"})])   # order doesn't matter

Output:

2 [['a', 'b'], ['c']]
3

frozenset is the immutable version of set. It is hashable, and two frozensets with the same members are equal whatever the order they were built in, which makes it a good key for unordered pairs. You cannot add() to it; build a new one instead.

3. Nested braces: {{...}} is a set of a set

# Easy to miss: {x} is a set literal, so {{"a"}} is a set containing a set
try:
    nested = {{"a", "b"}}
except TypeError as e:
    print("TypeError:", e)
print({frozenset({"a", "b"})})

Output:

TypeError: unhashable type: 'set'
{frozenset({'b', 'a'})}

{x} is a set literal, so double braces try to put a set inside a set. This often comes from f-string or template habits, where {{ means a literal brace. Note that the display order of set members varies between runs (string hashing is randomised).

Related: unhashable type: 'list' · unhashable type: 'dict' · unhashable type: 'slice'.

Frequently asked questions

Why is a set unhashable?

Sets are mutable, so their hash could change. Python only allows hashable values as set members and dict keys.

How do I make a set of sets?

Convert the inner sets to frozenset: {frozenset(s) for s in list_of_sets}.

Can I use a set as a dictionary key?

Not directly; use frozenset(s) as the key. Two frozensets with the same members compare and hash equal.

Written with AI assistance. Measured on 2026-10-23 with Python 3.13.5 on Debian: a set inside a set, a set as a dict key, set.add(set), the frozenset fix, and a nested {{...}} literal. Every block was run verbatim and its output pasted unedited. PlainHeaders is our extension; Python is free.