Stop the Crash: Fixing 'RuntimeError: generator raised StopIteration' in Python 3.7+

intermediate🐍 Python2026-04-03| Python 3.7 and newer (including 3.10, 3.11, 3.12, and 3.13) running on Linux, Windows, or macOS.

Error Message

RuntimeError: generator raised StopIteration
#python#generator#stopiteration#pep479#runtime-error

TL;DR: The Quick Fix

If your code broke after migrating to a modern Python version, you can usually fix it with a quick syntax change. You have two primary options:

  • Replace raise StopIteration with return: In modern Python, return is the correct way to signal a generator is finished.
  • Protect next() calls: If you use next(iterator) inside a generator, it will eventually throw StopIteration. Change this to next(iterator, None) to handle the end of the stream gracefully.
# ❌ BROKEN (Python 3.7+)
def my_generator(items):
    for i in items:
        if i == "stop":
            raise StopIteration # This now triggers a RuntimeError
        yield i

# ✅ FIXED
def my_generator(items):
    for i in items:
        if i == "stop":
            return # The proper way to exit
        yield i

The Root Cause: Why did it break?

Back in the Python 2.x and early 3.x days, raising StopIteration was the standard way to exit a generator. It worked, but it was dangerous. If a bug inside your generator triggered a StopIteration by mistake—perhaps from a broken list comprehension—the loop calling it would simply stop. You would lose data without any warning that a crash had occurred.

PEP 479 changed this behavior to make code more robust. Starting as the default in Python 3.7 (released in 2018), any StopIteration that leaks out of a generator is automatically converted into a RuntimeError. This forces you to handle the exit explicitly rather than letting exceptions bubble up silently.

You will see this specific traceback:

RuntimeError: generator raised StopIteration

Common Scenarios and Solutions

1. Manual StopIteration

Legacy scripts often use raise StopIteration to break logic. This is now a syntax "anti-pattern."

The Fix: Use return. In a generator, return doesn't just exit the function; it tells the iterator that there are no more values to yield. It is clean, readable, and PEP 479 compliant.

2. The 'Hidden' Killer: Unhandled next() calls

This is the most frequent cause of unexpected crashes. If you call next() on an internal iterator and it runs out of items, it raises StopIteration. Python 3.7+ catches this leaving your generator and kills the process with a RuntimeError.

# ❌ DANGEROUS CODE
def process_pairs(data):
    it = iter(data)
    while True:
        # If 'data' has an odd number of items, next(it) crashes here
        val1 = next(it) 
        val2 = next(it)
        yield val1 + val2

The Fix: Always provide a default value to next() or use a try/except block.

# ✅ SAFE CODE
def process_pairs(data):
    it = iter(data)
    while True:
        val1 = next(it, None)
        val2 = next(it, None)
        # Check if we hit the end of the data
        if val1 is None or val2 is None:
            return 
        yield val1 + val2

3. Nested Generators (yield from)

When using yield from sub_generator(), the error often lives inside the child function. If the sub-generator raises StopIteration, the parent will crash too.

The Fix: Audit your sub-generators. Ensure every function in the chain uses return to terminate.

Verification: Confirming the Fix

To ensure your generator is safe, try exhausting it completely using the list() constructor. This forces the generator to run until it hits its exit logic.

# Simple validation script
try:
    gen = my_generator_function([1, 2, 3])
    results = list(gen)
    print(f"Success! Processed {len(results)} items.")
except RuntimeError as e:
    if "generator raised StopIteration" in str(e):
        print("Error: The PEP 479 issue still exists.")
    else:
        print(f"Caught a different error: {e}")

If list(gen) finishes without a RuntimeError, your code is fully compatible with modern Python versions.

Further Reading

Related Error Notes