mutated-while-iterating

A loop resizes the collection it walks.

Group
Code
Runs
On by default
Level
warning
Reads
Every file
Fix
None: whether to walk a copy or build a new collection depends on which elements the loop is meant to see

Why

The loop's body adds to or removes from the collection the loop is walking, and the walk then takes another step. Depending on the collection, the walk skips the element after a removed one, visits added elements, or raises an error. Walk a copy, such as list(xs), or build a new collection instead of changing the one being walked. Each finding lists the language assumptions it relied on.

The fix, as Ur suggests it: walk a copy of the collection, or build a new one.

For agents

Never add to or remove from a collection inside a loop that walks it; walk a copy or build a new collection.

Example

app/cart.py
def remove_sold_out(cart, stock):
    for item in cart:
        if stock.get(item, 0) == 0:
            cart.remove(item)
    return cart
What Ur prints
$ ur lint --rule mutated-while-iterating
warning[mutated-while-iterating]: `remove` removes from `cart` while a loop walks it, and the walk may skip elements or raise an error
  --> ./app/cart.py:4:13 function `remove_sold_out`
  = step 1: ./app/cart.py:2:17 the loop walks `cart`
  = step 2: ./app/cart.py:4:13 the body removes from it, and the walk takes another step
  = assumes: resizing-methods
  = hint: walk a copy of the collection, or build a new one
  = see: The Python Tutorial, 4.2. for Statements, Python Software Foundation, Python 3.14 documentation (2025-10-07), https://docs.python.org/3.14/tutorial/controlflow.html#for-statements
0 clone classes and 1 protocol finding in 1 file

Try it in the playground →

Source

The Python Tutorial, 4.2. for Statements, Python Software Foundation, Python 3.14 documentation (2025-10-07).