RuntimeError, what it means and how to fix it

RuntimeError is the category for errors detected at runtime that belong nowhere narrower: the interpreter and libraries raise it for a miscellany of contract violations, and your own code can raise it the same way. Because it is a family rather than one error, the message text is the diagnosis: read it literally. The most frequently met member by far is “RuntimeError: dictionary changed size during iteration.”

What it looks like

scores = {"a": 90, "b": 75}
for name in scores:
    if scores[name] < 80:
        del scores[name]        # mutating while iterating

Traceback (most recent call last):
  File "main.py", line 4, in <module>
RuntimeError: dictionary changed size during iteration

# fixed: iterate over a snapshot of the keys
for name in list(scores):
    if scores[name] < 80:
        del scores[name]

The fix, in order

  1. Read the message as the error's real name. RuntimeError alone says nothing; “dictionary changed size during iteration”, “Event loop is closed”, “generator raised StopIteration”, and “can't start new thread” are different errors sharing a class, each with its own fix.
  2. For dictionary or set mutation during iteration: iterate over a copy (for k in list(d)), or collect the changes and apply them after the loop. Adding, deleting, or sometimes resizing while iterating invalidates the iterator's view.
  3. For event loop closed (asyncio): something scheduled work on a loop that already shut down, common when asyncio.run is called twice in notebooks or when objects outlive the loop that created them. Create the loop work inside one asyncio.run call.
  4. For generator raised StopIteration: a generator let a StopIteration bubble out of its body (often from next() inside the generator); Python converts it to RuntimeError by design. Catch it inside the generator or return instead.
  5. When your own code raises RuntimeError, treat it as a deliberate assertion of an impossible state; debug by logging the state that was assumed impossible, not by catching and continuing past it.

If it still happens

A RuntimeError whose message you do not recognize is almost always a library enforcing its own contract (a closed resource being used, a re-entrant call, a thread misuse). Search the exact message text; it is distinctive by design, and the library's documentation usually has a section for it.

Common questions

Is RuntimeError one error or many? Many. It is the default bucket for runtime failures without a narrower class, so two RuntimeErrors can have nothing in common but the class name. Always work from the message, never from the class alone.

Why can't I modify a dictionary while looping over it? Iterators track the container's size and internal layout to know where they are. Changing the size mid-loop makes the iterator's position meaningless, so Python refuses rather than silently skipping or repeating entries. Iterating a snapshot list gives the loop a stable view while the original changes.

Related errors

StopIteration · RecursionError · OSError