Python
RuntimeError
Error Handling
Dictionary Iteration
Debugging

How to avoid RuntimeError dictionary changed size during iteration error?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

The RuntimeError: dictionary changed size during iteration occurs when you add or remove keys from a dictionary while iterating over it with a for loop. Python raises this error because modifying the dictionary's size invalidates the iterator. The fix is to iterate over a copy of the keys (list(d.keys())), use a dictionary comprehension to create a new dictionary, or collect keys to modify and apply changes after the loop.

The Error

python
1data = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
2
3# RuntimeError: dictionary changed size during iteration
4for key in data:
5    if data[key] % 2 == 0:
6        del data[key]

Python detects that the dictionary's size changed during iteration and raises RuntimeError to prevent undefined behavior.

Fix 1: Iterate Over a Copy of Keys

Create a snapshot of the keys before iterating:

python
1data = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
2
3for key in list(data.keys()):  # list() creates a copy
4    if data[key] % 2 == 0:
5        del data[key]
6
7print(data)  # {'a': 1, 'c': 3}

list(data.keys()) creates an independent list of keys at that moment. Deleting from data does not affect the list being iterated.

Fix 2: Dictionary Comprehension (Create New Dict)

Build a new dictionary with only the desired entries:

python
1data = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
2
3# Keep only odd values
4data = {k: v for k, v in data.items() if v % 2 != 0}
5print(data)  # {'a': 1, 'c': 3}

This is the most Pythonic approach for filtering. It creates a new dictionary without modifying the original during iteration.

Fix 3: Collect Keys, Then Modify

Separate the iteration and modification into two steps:

python
1data = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
2
3# Step 1: Collect keys to delete
4keys_to_delete = [k for k, v in data.items() if v % 2 == 0]
5
6# Step 2: Delete after iteration is complete
7for key in keys_to_delete:
8    del data[key]
9
10print(data)  # {'a': 1, 'c': 3}

This is useful when deletion logic is complex or has side effects.

Fix 4: Copy with dict() or .copy()

python
1data = {'a': 1, 'b': 2, 'c': 3}
2
3# Iterate over a shallow copy
4for key, value in dict(data).items():
5    if value < 2:
6        del data[key]
7
8# Or use .copy()
9for key in data.copy():
10    if data[key] < 2:
11        del data[key]

Both dict(data) and data.copy() create shallow copies. The loop iterates over the copy while modifications happen on the original.

Adding Keys During Iteration

The error also occurs when adding new keys:

python
1data = {'a': 1, 'b': 2}
2
3# RuntimeError: dictionary changed size during iteration
4for key in data:
5    data[key + '_new'] = data[key] * 10
6
7# Fix: iterate over a copy
8for key in list(data.keys()):
9    data[key + '_new'] = data[key] * 10
10
11print(data)
12# {'a': 1, 'b': 2, 'a_new': 10, 'b_new': 20}

Modifying Values Is Safe

Changing existing values does not change the dictionary's size, so it is allowed:

python
1data = {'a': 1, 'b': 2, 'c': 3}
2
3# Safe: modifying values does not change size
4for key in data:
5    data[key] *= 10
6
7print(data)  # {'a': 10, 'b': 20, 'c': 30}

Only adding new keys or deleting existing keys triggers the error.

Nested Dictionary Iteration

python
1config = {
2    'database': {'host': 'localhost', 'port': 5432, 'debug': True},
3    'cache': {'host': 'redis', 'ttl': 300, 'debug': True}
4}
5
6# Remove 'debug' key from all nested dicts
7for section in config:
8    for key in list(config[section].keys()):
9        if key == 'debug':
10            del config[section][key]
11
12print(config)
13# {'database': {'host': 'localhost', 'port': 5432},
14#  'cache': {'host': 'redis', 'ttl': 300}}

Apply list() on the inner dictionary's keys, not the outer one (unless you are modifying both).

defaultdict and Counter

The same error applies to defaultdict and Counter:

python
1from collections import defaultdict
2
3dd = defaultdict(int, {'a': 1, 'b': 2, 'c': 3})
4
5# Same fix: iterate over a copy
6for key in list(dd.keys()):
7    if dd[key] < 2:
8        del dd[key]

Common Pitfalls

  • Iterating over data.keys() instead of list(data.keys()): In Python 3, dict.keys() returns a view, not a copy. The view reflects changes to the dictionary, so deleting during iteration still raises the error. Wrap in list() to create an independent copy.
  • Forgetting that for key in data iterates over keys: for key in data is equivalent to for key in data.keys(). Both return a view that is invalidated by size changes. Always copy before modifying.
  • Assuming modifying a value triggers the error: Only structural changes (add/delete keys) trigger the error. data[key] = new_value for an existing key is safe because the dictionary size does not change.
  • Using data.copy() for deeply nested dictionaries: .copy() creates a shallow copy. If you modify nested dictionaries inside the copy, the changes affect the original too. For deep structures, use copy.deepcopy() if you need full isolation.
  • Catching RuntimeError instead of fixing the iteration: Wrapping the loop in try/except RuntimeError hides the bug and may skip items. Fix the iteration pattern instead of suppressing the error.

Summary

  • The error occurs when adding or deleting dictionary keys during iteration
  • Use list(data.keys()) to iterate over a copy of keys
  • Use dictionary comprehensions to create a filtered new dictionary
  • Collect keys to delete in a separate list, then delete after the loop
  • Modifying existing values (without adding/deleting keys) is always safe

Course illustration
Course illustration

All Rights Reserved.