StopIteration, what it means and how to fix it

StopIteration is the signal an iterator raises to say “no more items.” For loops consume it silently, which is why you only meet it when driving iterators by hand with next(), or when it escapes from a generator in an unexpected place. It is less an error than a protocol message that leaked: somewhere, code asked for one item too many and nothing caught the signal.

What it looks like

lines = iter(["first"])
print(next(lines))        # 'first'
print(next(lines))        # nothing left

Traceback (most recent call last):
  File "main.py", line 3, in <module>
StopIteration

# fixed: supply a default for the exhausted case
print(next(lines, None))

The fix, in order

  1. Give next() a default when exhaustion is a normal outcome: next(it, None) (or a sentinel) returns the default instead of raising, turning an exception path into a value you can test.
  2. Prefer for loops over manual next() when you do not need manual control. The loop exists to handle exactly this signal; hand-driven iteration re-implements it and inherits its edge cases.
  3. If the traceback says “generator raised StopIteration”, the StopIteration escaped from inside a generator's body (often an uncaught next() call in there). Since Python 3.7, that is converted to RuntimeError by design (PEP 479), because a leaked StopIteration silently terminates the consuming loop and hides bugs. Catch it inside the generator and return.
  4. Inside your own generators, end with return, not raise StopIteration. Returning is the correct, explicit way to finish; raising StopIteration inside a generator is the precise anti-pattern the conversion exists to catch.
  5. Check for exhausted iterators being reused. An iterator is one-shot: a second loop over the same iterator (not the same list) yields nothing. If a “missing data” bug smells like this, materialize the source into a list once and iterate that.

If it still happens

Framework code (parsers, streaming readers, custom iterators) can raise StopIteration from deep inside when input ends earlier than its grammar expects. There the fix is in the input: the stream ended mid-structure, and the iterator's exhaustion is the messenger.

Common questions

Why does a for loop never show me StopIteration? Because the loop's machinery is built on it: each iteration calls next(), and StopIteration is the agreed signal to end the loop normally. You see the exception only when next() is called outside that machinery, by hand or inside a generator.

What changed about StopIteration in generators? PEP 479 (default from Python 3.7): a StopIteration bubbling out of a generator frame becomes RuntimeError: generator raised StopIteration. Before that, it quietly ended the generator, which masked real bugs where next() failed unexpectedly inside generator code.

Related errors

RuntimeError · IndexError · KeyError