RuntimeError: dictionary changed size during iteration
Python · updated 2026-11-03
Raised on the next step of a loop over a dict (or its keys, values or items) after the dict gained or lost a key inside the loop. Sets raise the same error with "Set changed size during iteration".
Traceback (most recent call last):
File "/app/cache.py", line 21, in evict_expired
for key, entry in self._items.items():
RuntimeError: dictionary changed size during iterationQuick fix
Iterate over a snapshot when you need to delete or add keys: for key, entry in list(d.items()). Or build a new dict with a comprehension and replace the old one. If another thread changes the dict, protect it with a lock.
- Find the loop on the failing line and the line inside it that adds or deletes keys.
- Loop over list(d.items()) (or list(d)) so the loop has its own copy.
- For filtering, build a new dict: d = {k: v for k, v in d.items() if keep(v)}.
- If threads share the dict, guard reads and writes with a threading.Lock.
# Before: deleting while iterating the same dict
# for key, entry in cache.items():
# if entry.expired:
# del cache[key]
for key, entry in list(cache.items()): # iterate a snapshot
if entry.expired:
del cache[key]
# Or replace it in one step
cache = {k: e for k, e in cache.items() if not e.expired}Why it happens
Deleting keys inside the loop
Removing expired or invalid entries with del or pop while looping over the same dict is the most common case.
Adding keys inside the loop
Setting d[new_key] while iterating, including through a defaultdict whose missing-key lookup creates the key, changes the size just the same.
Another thread changing the dict
A background thread or a callback that writes to a shared dict while the main thread loops over it raises this error at random, which makes it look flaky.
Iterating over module or object state that changes
Looping over sys.modules, vars(obj) or globals() while code imports a module or sets an attribute can change those dicts under the loop.
Other fixes
- Changing values for existing keys is fine: only adding or removing keys breaks the loop.
- Collect keys to delete in a list during the loop and delete them after it.
Same error, other wording
- RuntimeError: Set changed size during iteration
- RuntimeError: OrderedDict mutated during iteration
- RuntimeError: dictionary keys changed during iteration
Let your coding agent catch the next one
Runheed records this error in production with the stack trace, the breadcrumbs before it and the agent session that shipped the code, and serves it to Claude Code, Cursor or any MCP client. With it connected, you can ask:
> Find the 'dictionary changed size during iteration' errors in production, show me each loop and what changes the dict inside it, and fix it by iterating a snapshot or rebuilding the dict.
Related errors
- KeyError: 'email'
- TypeError: 'NoneType' object is not iterable
- RecursionError: maximum recursion depth exceeded
References: Python docs: dictionary view objects, Python docs: built-in exceptions (RuntimeError)