The Error
You're looping over a dictionary and deleting or adding keys inside that same loop. Python immediately throws:
RuntimeError: dictionary changed size during iteration
Here's a classic example β filtering out even values by deleting keys:
data = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
for key in data:
if data[key] % 2 == 0:
del data[key] # RuntimeError here
Adding keys mid-loop hits the same wall:
cache = {'x': 10}
for key in cache:
cache[key + '_copy'] = cache[key] # RuntimeError: dictionary changed size during iteration
Why This Happens
Python's for key in dict creates an internal iterator that watches the dictionary's size. Add or remove a key, and the size changes. Python detects this immediately and raises RuntimeError β on purpose.
Without this guard, the iterator could skip keys, visit the same key twice, or crash unpredictably. Python 3 made this a hard error. Python 2 let you do it silently, which led to data corruption that was nearly impossible to debug.
Important distinction: changing a value for an existing key is perfectly fine. Only adding or removing keys (which changes the dict's size) triggers this error.
Quick Fix: Iterate Over a Copy
Wrap the loop target in list(). That snapshots the keys into a plain list before iteration starts β the dict can change freely underneath it.
data = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
for key in list(data.keys()): # list() creates a snapshot
if data[key] % 2 == 0:
del data[key]
print(data) # {'a': 1, 'c': 3}
Need both key and value? Same trick works with .items():
for key, value in list(data.items()):
if value % 2 == 0:
del data[key]
One-line change, zero new concepts. Good for quick patches on existing code.
Cleaner Fix: Dict Comprehension
For filtering use cases, skip mutation entirely and build a fresh dict:
data = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
data = {k: v for k, v in data.items() if v % 2 != 0}
print(data) # {'a': 1, 'c': 3}
No in-place mutation means no risk of the error. It's also easier to read β the condition is right there in the expression. This is the preferred approach for filtering dictionaries in Python 3.
Fix for Adding Keys During Iteration
Need to generate new entries while processing existing ones? Collect them in a separate dict, then merge after the loop.
cache = {'x': 10, 'y': 20}
# Wrong: adding to cache inside the loop
# for key in cache:
# cache[key + '_copy'] = cache[key] # RuntimeError
# Correct: stage new entries separately
new_entries = {}
for key, value in cache.items():
new_entries[key + '_copy'] = value
cache.update(new_entries)
print(cache) # {'x': 10, 'y': 20, 'x_copy': 10, 'y_copy': 20}
The loop only reads from cache. new_entries absorbs all writes. Then a single update() merges everything safely.
Fix for Renaming Keys
Renaming keys is trickier β you're both adding and removing. Use a collect-then-apply pattern:
config = {'debug_mode': True, 'debug_level': 3, 'timeout': 30}
# Rename all keys starting with 'debug_' β 'log_'
to_rename = {k: k.replace('debug_', 'log_') for k in config if k.startswith('debug_')}
for old_key, new_key in to_rename.items():
config[new_key] = config.pop(old_key)
print(config) # {'timeout': 30, 'log_mode': True, 'log_level': 3}
First pass: figure out what to rename (no mutations). Second pass: apply the renames. Clean separation, no iterator conflicts.
Nested Dicts
Modifying an inner dict while iterating the outer one is fine β the outer iterator doesn't care. But if the outer dict itself changes size, you'll hit the error as usual:
users = {
'alice': {'score': 10, 'active': True},
'bob': {'score': 5, 'active': False},
'charlie': {'score': 8, 'active': True},
}
# Remove inactive users β safe because we copy the outer keys first
for name in list(users):
if not users[name]['active']:
del users[name]
print(users) # {'alice': {...}, 'charlie': {...}}
Verification
After applying the fix, run the code and assert the result is what you expect:
data = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
data = {k: v for k, v in data.items() if v % 2 != 0}
assert data == {'a': 1, 'c': 3}, f"Unexpected result: {data}"
print("OK:", data)
Expected output:
OK: {'a': 1, 'c': 3}
No RuntimeError, correct keys and values β you're done.
Summary
- Never add or remove keys from a dict while iterating over it directly.
- Quick fix: wrap the loop target in
list()β e.g.for key in list(data). - Preferred fix for filtering: use a dict comprehension to produce a new dict instead of mutating the old one.
- For adding keys: collect new entries in a separate dict, then call
.update()after the loop. - For renaming keys: build a rename map first, apply it in a second pass.

