RuntimeError
An operation that was legal on its own became illegal because of the state around it — the classic is changing a dict while a for loop is walking it.
Demo
stock = {'apple': 3, 'pear': 0, 'plum': 0} for name in stock: if stock[name] == 7: del stock[name] stock
In Trigger, deleting 'apple' (qty 3) is the only change, yet the loop still dies: the dict iterator checks the size on every next() call, so the error comes from the step AFTER the del. Handle deletes the same items without complaint, because list(stock) copied the keys before the loop started.
Constructor
| Name | Type | Required | Description |
|---|---|---|---|
| *args | object | no | Usually one string saying what went wrong. Stored in e.args; str(e) is that string. |
Attributes
| Attribute | Type | Meaning |
|---|---|---|
| args | tuple | The constructor arguments — normally the one message string. |
| __cause__ | BaseException | None | For "generator raised StopIteration" this is the original StopIteration. |
| __context__ | BaseException | None | The exception being handled when this one was raised (implicit chaining). |
Common patterns
in_stock = {name: qty for name, qty in stock.items() if qty > 0}
for name in list(stock): if stock[name] == 0: del stock[name]
class Connection: def send(self, data): if self.closed: raise RuntimeError('send() on a closed connection') self._sock.sendall(data)
Examples
Pitfalls
prices = {'a': 5, 'b': 0} for k in prices: if prices[k] == 0: del prices[k] prices
prices = {'a': 5, 'b': 0} for k in prices: prices[k] = prices[k] * 2 prices
def firsts(rows): for row in rows: yield next(iter(row)) list(firsts([[1], []]))
def firsts(rows): for row in rows: yield next(iter(row), None) list(firsts([[1], []]))
class Base: def save(self): raise NotImplementedError try: Base().save() status = 'saved' except RuntimeError: status = 'busy, retry later' status
class Base: def save(self): raise NotImplementedError try: Base().save() status = 'saved' except RuntimeError as e: if isinstance(e, NotImplementedError): raise status = 'busy, retry later' status
When to use
- Your object is called in a state where the operation cannot work (closed, not started, already finished)
- An invariant broke at runtime and no more specific exception (ValueError, TypeError, …) fits
- Bad argument values → ValueError; wrong types → TypeError
- Methods a subclass must provide → NotImplementedError
- Catching it broadly — you also catch RecursionError and NotImplementedError
Notes
FAQ
Do not add or remove keys of a dict while a for loop is iterating it. Either loop over a snapshot — for k in list(d): — or build a new dict with a comprehension: d = {k: v for k, v in d.items() if keep(v)}. Updating the value of an existing key inside the loop is fine. The same applies to sets (Set changed size during iteration) and to d.keys(), d.values() and d.items(), which are live views of the dict.