Code review example: Deleting from a dict while iterating it raises RuntimeError in Python
Retired from the DiffDojo practice pool in October 2026; the review below is the canonical answer. An AI wrote this pull request for the task underneath. Read the diff and decide what you would say before you scroll to the review.
The task
Evict expired cache entries
- evict_expired(cache, now) removes every entry whose expiry is at or before
now. cachemaps key -> (value, expires_at).- Entries that have not expired must remain.
Adds cache eviction: iterates the cache and deletes any entry that has expired. Removes the stale keys in one pass.
The diff
app/cache/evict.py+4The review
Issue 1: Deleting from a dict while iterating it raises RuntimeError
- You can't resize a dict mid-iteration.
del cache[key]insidefor ... in cache.items()changes the dict's size, and Python raisesRuntimeError: dictionary changed size during iterationon the very next loop step. - It fails exactly when it has work to do. With nothing expired, the loop never deletes and completes fine, so a quick test with fresh entries passes. The first real expired entry crashes it.
- The bug is iterating and mutating the same object. The loop needs a view that won't change underneath it.
The fix: iterate over a snapshot, for key, (_, expires_at) in list(cache.items()):, or collect the expired keys first and delete them in a second pass.
The fix
def evict_expired(cache: dict, now: float) -> None:
expired = [
key for key, (_, expires_at) in cache.items() if expires_at <= now
]
for key in expired:
del cache[key]Why it matters
Deleting or adding dict/set keys inside a loop over the same collection → RuntimeError. Snapshot with list(...) or collect-then-mutate.
Real-world stakes: the eviction job runs fine in tests with unexpired fixtures and throws in production the moment a real entry ages out, so the cache never gets cleaned and grows unbounded, or the request that triggered eviction 500s. Mutating a container mid-iteration is a classic Python edge-case bug, and assistants write it because the single-pass del inside the loop reads like the most direct thing.
The verdict
Request changes. Deleting from a dict while iterating it raises RuntimeError
Try a live one
Python PRs you have not seen the answer to.
Leave inline comments and a verdict, then see what you caught. Free, no signup.
Review a Python PR →