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.
Demo
it = iter(['a', 'b']) first = next(it) second = next(it) (first, second)
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
| Name | Type | Required | Description |
|---|---|---|---|
| value | object | no (None) | Stored as e.value (and in e.args). A generator's return value arrives here. |
Attributes
| Attribute | Type | Meaning |
|---|---|---|
| value | object | The first constructor argument, or None. When a generator executes return x, the StopIteration it raises has value x. |
| args | tuple | Constructor arguments; empty for iterator exhaustion. |
| __cause__ | BaseException | None | On the PEP 479 RuntimeError, __cause__ is the original StopIteration. |
Common patterns
admin = next((u for u in users if u.is_admin), None)
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
def pairs(items): it = iter(items) while True: try: a, b = next(it), next(it) except StopIteration: return yield a, b
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
Pitfalls
def pairs(items): it = iter(items) while True: a = next(it) b = next(it) yield (a, b) list(pairs([1, 2, 3, 4]))
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]))
nums = map(int, ['1', '2']) total = sum(nums) next(nums)
nums = list(map(int, ['1', '2'])) total = sum(nums) (total, nums[0])
it = iter([1, 2]) list(next(it) for _ in range(3))
from itertools import islice it = iter([1, 2]) list(islice(it, 3))
When to use
- 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
- 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
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).