RuntimeError: generator raised StopIteration (tested)
RuntimeError: generator raised StopIteration means a StopIteration, almost always from a bare next(), escaped from inside a generator. We measured why that's an error since Python 3.7, the fixes, and the related case where nothing is raised at all.
1. What triggers it
def first_words(lines):
for line in lines:
it = iter(line.split())
yield next(it) # empty line -> StopIteration inside a generator
try:
print(list(first_words(["hello world", "", "bye"])))
except RuntimeError as e:
print(type(e).__name__ + ":", e)
print("cause:", repr(e.__cause__))
Output:
RuntimeError: generator raised StopIteration
cause: StopIteration()
Measured on Python 3.13.5: a bare next() inside a generator ran out (here, on an empty line) and the StopIteration escaped from the generator body. Since Python 3.7 (PEP 479), Python turns that into RuntimeError: generator raised StopIteration; the original StopIteration is kept as __cause__, so the traceback shows the next() line under "The above exception was the direct cause".
2. Old code that used next() to end the generator
def pairs(it):
it = iter(it)
while True:
a = next(it) # pre-3.7 this quietly ended the generator
b = next(it)
yield a, b
try:
print(list(pairs([1, 2, 3, 4])))
except RuntimeError as e:
print("RuntimeError:", e)
def pairs_fixed(it):
it = iter(it)
while True:
try:
a = next(it)
b = next(it)
except StopIteration:
return # end the generator explicitly
yield a, b
print(list(pairs_fixed([1, 2, 3, 4])))
print(list(pairs_fixed([1, 2, 3, 4, 5]))) # odd item dropped
print(list(zip(*[iter([1, 2, 3, 4, 5])] * 2)))
Output:
RuntimeError: generator raised StopIteration
[(1, 2), (3, 4)]
[(1, 2), (3, 4)]
[(1, 2), (3, 4)]
Measured: before 3.7 a StopIteration from next() quietly ended the generator, and a lot of older code (and Stack Overflow answers) relied on that. Now you have to end it yourself: catch StopIteration and return. For this particular job, zip(*[iter(xs)] * 2) or itertools.batched(xs, 2) (3.12+) do it without a generator; our fixed version and zip both drop the odd last item, while batched keeps it as (5,) (checked separately).
3. Fix: next() with a default, and return instead of raise
def first_words(lines):
for line in lines:
w = next(iter(line.split()), None) # default instead of StopIteration
if w is not None:
yield w
print(list(first_words(["hello world", "", "bye"])))
def gen():
yield 1
raise StopIteration # never do this; use return
try:
print(list(gen()))
except RuntimeError as e:
print("raise StopIteration:", e)
Output:
['hello', 'bye']
raise StopIteration: generator raised StopIteration
next(iterator, None) returns the default instead of raising, which is usually what you meant. To finish a generator early, write return; raise StopIteration inside a generator is now always the RuntimeError above.
4. Outside generators: map() silently truncates
def first(xs):
return next(iter(xs)) # StopIteration on an empty list
# In a plain call it escapes as StopIteration:
try:
first([])
except StopIteration as e:
print("plain call:", repr(e))
# Inside map()/filter(), the SAME exception silently ends the iteration:
rows = [[1], [], [3]]
print("map:", list(map(first, rows)))
# A generator expression converts it to RuntimeError instead:
try:
print(list(first(r) for r in rows))
except RuntimeError as e:
print("genexp:", e)
# A list comprehension lets the StopIteration escape:
try:
print([first(r) for r in rows])
except StopIteration:
print("listcomp: StopIteration escaped")
Output:
plain call: StopIteration()
map: [1]
genexp: generator raised StopIteration
listcomp: StopIteration escaped
Measured: PEP 479 only covers generators (and generator expressions). The same StopIteration raised by a function called from map() or filter() just ends the iteration: we got [1] from three rows with no error, losing the third row. A list comprehension lets it escape as a plain StopIteration. If a helper calls next(), give it a default or catch StopIteration inside the helper.
Frequently asked questions
What does RuntimeError: generator raised StopIteration mean?
A StopIteration escaped from inside a generator, usually from a bare next() on an exhausted iterator. Since Python 3.7 (PEP 479) that becomes a RuntimeError; we measured it with the original StopIteration as __cause__.
How do I fix generator raised StopIteration?
Use next(it, default) or wrap next() in try/except StopIteration and return. Replace raise StopIteration with return. We measured both fixes on Python 3.13.5.
Why did my code work in Python 3.6 but not 3.7+?
Before 3.7, a StopIteration inside a generator silently ended it. PEP 479 made that an error by default in 3.7; we measured old-style pairing code raising RuntimeError on 3.13.
Can StopIteration cause silent data loss?
Yes, outside generators. We measured a function raising StopIteration inside map() ending the iteration early: three rows in, one result out, no error.
Written with AI assistance. Measured on 2026-10-09 with Python 3.13.5 on Linux: bare next() in a generator, old-style pairing code, the next(it, None) and return fixes, raise StopIteration in a generator, and the same StopIteration under map(), a generator expression and a list comprehension. Every block was run verbatim and its output pasted from that run. Pre-3.7 behaviour is from PEP 479, not re-tested. PlainHeaders is our extension; everything else here is built-in Python.