AttributeError: 'dict' object has no attribute 'iteritems' (tested fix)
AttributeError: 'dict' object has no attribute 'iteritems' (or 'has_key', 'iterkeys', 'itervalues') means Python 2 code is running on Python 3. We checked every removed method on Python 3.13 and listed the exact replacement for each, plus one difference to watch for when you swap them.
1. The Python 2 dict methods that no longer exist
Python 3 removed five dict methods. Code written for Python 2, or copied from an old tutorial, still calls them:
d = {"a": 1, "b": 2}
for call in ["d.iteritems()", "d.iterkeys()", "d.itervalues()", "d.has_key('a')", "d.viewkeys()"]:
try:
eval(call)
print(f"{call:18} OK")
except AttributeError as e:
print(f"{call:18} AttributeError: {e}")
Output:
d.iteritems() AttributeError: 'dict' object has no attribute 'iteritems'
d.iterkeys() AttributeError: 'dict' object has no attribute 'iterkeys'
d.itervalues() AttributeError: 'dict' object has no attribute 'itervalues'
d.has_key('a') AttributeError: 'dict' object has no attribute 'has_key'
d.viewkeys() AttributeError: 'dict' object has no attribute 'viewkeys'
The replacements are one-to-one:
d.iteritems()becomesd.items()d.iterkeys()becomesd.keys(), or justfor k in dd.itervalues()becomesd.values()d.has_key(k)becomesk in dd.viewkeys()/viewitems()/viewvalues()becomekeys()/items()/values()
2. items() in Python 3 is a live view, not a list
Python 3's items() doesn't copy anything (like Python 2's iteritems()), so swapping the name is safe for loops. It is a live view of the dict, though, and that matters when the dict changes:
d = {"a": 1, "b": 2}
print("for k, v in d.items():", [(k, v) for k, v in d.items()])
print("'a' in d:", "a" in d, "| 'z' in d:", "z" in d)
items = d.items() # a live view, not a copy
d["c"] = 3
print("view sees new key:", list(items))
try:
for k in d:
d[k + "2"] = 0 # changing size while iterating
except RuntimeError as e:
print("RuntimeError:", e)
for k in list(d): # iterate over a copy of the keys instead
if k.endswith("2"):
del d[k]
print(d)
Output:
for k, v in d.items(): [('a', 1), ('b', 2)]
'a' in d: True | 'z' in d: False
view sees new key: [('a', 1), ('b', 2), ('c', 3)]
RuntimeError: dictionary changed size during iteration
{'a': 1, 'b': 2, 'c': 3}
The view showed the key added after it was created. Adding keys while looping over the dict raises RuntimeError. Loop over list(d) (or list(d.items())) when the loop body adds or removes keys.
3. When the error is inside a library
If the traceback ends in a third-party package, that package (or that version of it) only supports Python 2. Upgrade it with pip install -U <package>, or replace it if it is abandoned. For your own old code, the 2to3 tool that used to do this rewrite was removed from Python in 3.13, so search for .iteritems(, .has_key( and the others and replace them by hand, using the list above. See also our collections has no attribute Mapping and str has no attribute decode guides, which are the other common Python 2 leftovers.
Frequently asked questions
What replaces dict.iteritems() in Python 3?
d.items(). In Python 3, items() returns a view that doesn't copy the dict, so it behaves like Python 2's iteritems().
What replaces dict.has_key() in Python 3?
The in operator: key in d. has_key() was removed in Python 3.
Can I still use 2to3 to fix this?
Not on Python 3.13: the 2to3 tool and lib2to3 were removed. Replace the methods by hand or run the conversion with an older Python.
Written with AI assistance. Measured on 2026-10-22 on Debian with Python 3.13.5: iteritems, iterkeys, itervalues, has_key and viewkeys on a dict, items() view behaviour when the dict changes, and changing a dict while iterating. Every block was run verbatim and its output pasted unedited. PlainHeaders is our extension; Python is free.