StopIteration

The end-of-iterator signal: for loops absorb it silently, a bare next() lets it escape, and inside a generator it turns into RuntimeError.

InheritsBaseException›Exception›StopIteration
Control-flow exceptionPython 3 (all)Live demo
StopIteration([value])
Raised by
next(it) on an exhausted iterator, __next__() of your own iterators
Message
usually empty — the traceback ends in a bare StopIteration
Quick fix
next(it, default) — or a for loop
Watch out
inside a generator it becomes RuntimeError: generator raised StopIteration

Demo

Live evaluation
Two next() calls on an iterator. With fewer than two items the second (or first) call raises.
Try:
Inputs
itemslist[str]comma-separated
Code
it = iter(['a', 'b'])
first = next(it)
second = next(it)
(first, second)
Result
('a', 'b')

The traceback of a plain next() on an empty iterator ends in just StopIteration — no message, because iterators raise it with no arguments. Move the same calls into a generator (Raise) and you get RuntimeError: generator raised StopIteration instead: since Python 3.7 a StopIteration may not leak out of a generator, because it would silently end the loop that consumes it.

Constructor

NameTypeRequiredDescription
valueobjectno (None)Stored as e.value (and in e.args). A generator's return value arrives here.

Attributes

AttributeTypeMeaning
valueobjectThe first constructor argument, or None. When a generator executes return x, the StopIteration it raises has value x.
argstupleConstructor arguments; empty for iterator exhaustion.
__cause__BaseException | NoneOn the PEP 479 RuntimeError, __cause__ is the original StopIteration.

Common patterns

First item or a default
The idiomatic "first match" lookup — no exception handling needed.
admin = next((u for u in users if u.is_admin), None)
Writing an iterator class
__next__ signals the end by raising StopIteration; for loops, list() and sum() stop on it.
class Countdown:
    def __init__(self, n):
        self.n = n
    def __iter__(self):
        return self
    def __next__(self):
        if self.n <= 0:
            raise StopIteration
        self.n -= 1
        return self.n + 1
next() inside a generator, safely
Catch StopIteration and return — never let it escape the generator body.
def pairs(items):
    it = iter(items)
    while True:
        try:
            a, b = next(it), next(it)
        except StopIteration:
            return
        yield a, b
Async iterator end (StopAsyncIteration)
__anext__ of an async iterator signals the end with StopAsyncIteration; async for absorbs it.
class Ticker:
    def __init__(self, n):
        self.n = n
    def __aiter__(self):
        return self
    async def __anext__(self):
        if self.n == 0:
            raise StopAsyncIteration
        self.n -= 1
        return self.n

Examples

1. next() on an empty iterator
next(iter([]))
Returns
StopIteration
2. next() with a default
next(iter([]), 'done')
Returns
'done'
3. A generator's return value is e.value
def gen(): yield 1 return 'finished' g = gen() next(g) try: next(g) except StopIteration as e: r = e.value r
Returns
'finished'
4. for loops absorb it
class Countdown: def __init__(self, n): self.n = n def __iter__(self): return self def __next__(self): if self.n <= 0: raise StopIteration self.n -= 1 return self.n + 1 list(Countdown(3))
Returns
[3, 2, 1]
5. Inside a generator it becomes RuntimeError
def gen(): yield next(iter([])) try: list(gen()) except RuntimeError as e: r = (str(e), type(e.__cause__).__name__) r
Returns
('generator raised StopIteration', 'StopIteration')
6. yield from hands back the return value
def inner(): yield 1 return 'inner done' def outer(): result = yield from inner() yield result list(outer())
Returns
[1, 'inner done']
7. StopAsyncIteration from anext()
import asyncio async def agen(): yield 1 async def main(): it = agen() await anext(it) try: await anext(it) except StopAsyncIteration: return 'exhausted' asyncio.run(main())
Returns
'exhausted'
8. StopAsyncIteration is not a StopIteration
issubclass(StopAsyncIteration, StopIteration)
Returns
False

Pitfalls

1. Relying on StopIteration to end a generator
Before Python 3.7 an unguarded next() inside a generator quietly ended it. Now it is a RuntimeError — old code like this breaks.
unguarded next()
def pairs(items):
    it = iter(items)
    while True:
        a = next(it)
        b = next(it)
        yield (a, b)
list(pairs([1, 2, 3, 4]))
RuntimeError: generator raised StopIteration
catch and return
def pairs(items):
    it = iter(items)
    while True:
        try:
            a = next(it)
            b = next(it)
        except StopIteration:
            return
        yield (a, b)
list(pairs([1, 2, 3, 4]))
[(1, 2), (3, 4)]
2. Iterators are single-use
map(), filter(), zip(), generators and file objects are exhausted after one pass. The second consumer finds nothing.
reuse a map
nums = map(int, ['1', '2'])
total = sum(nums)
next(nums)
StopIteration
materialise once
nums = list(map(int, ['1', '2']))
total = sum(nums)
(total, nums[0])
(3, 1)
3. next() inside a generator expression
A generator expression is a generator, so PEP 479 applies there too. Take a bounded slice instead.
next() in genexp
it = iter([1, 2])
list(next(it) for _ in range(3))
RuntimeError: generator raised StopIteration
itertools.islice
from itertools import islice
it = iter([1, 2])
list(islice(it, 3))
[1, 2]

When to use

Use it
  • Ending __next__ in an iterator class you write
  • Reading a generator's return value (e.value) when driving it by hand
  • try/except StopIteration around next() when the empty case needs its own logic
Reach for something else
  • Just need a fallback → next(it, default)
  • Ending a generator → return (never raise StopIteration inside it)
  • Looping → a for loop handles the end for you

Notes

CPython impl
Python/intrinsics.c — stopiteration_error() converts a StopIteration escaping a generator into RuntimeError, with the original as __cause__
PEP 479
Change StopIteration handling inside generators — default since 3.7
Async
StopAsyncIteration (3.5+) is the async counterpart, raised by __anext__ and anext(); it is a sibling of StopIteration, not a subclass
Why an Exception
It subclasses Exception (unlike GeneratorExit), so a broad except Exception around next() will catch it

FAQ

A StopIteration escaped from inside a generator function or generator expression — usually from a bare next() call on an exhausted iterator. Since Python 3.7 (PEP 479) this is converted to RuntimeError instead of silently ending the generator. Fix it by catching StopIteration and using return, or by calling next(it, default).

History

3.3
Added the value attribute and the ability for generator functions to use it to return a value.
3.5
RuntimeError transformation available via from __future__ import generator_stop (PEP 479). StopAsyncIteration added.
3.7
PEP 479 enabled for all code: a StopIteration raised in a generator is transformed into RuntimeError.