A contextmanager generator represents one entry and one exit. It must yield one value; it cannot rerun the caller with block by yielding again after an exception.
Check the failure with a small fixture
A generator-based context manager tries to yield another resource after an error inside the with block.
from contextlib import contextmanager
@contextmanager
def wrong():
try:
yield "first"
except ValueError:
yield "second"
try:
with wrong():
raise ValueError("body failed")
except RuntimeError as exc:
assert "generator didn't stop" in str(exc)
print("second yield rejected")
events = []
@contextmanager
def owned_resource():
events.append("open")
try:
yield "resource"
finally:
events.append("close")
try:
with owned_resource():
raise ValueError("body failed")
except ValueError:
pass
assert events == ["open", "close"]What the result means
The exception from the body is thrown back into the generator at its yield point. The incorrect handler yields again instead of finishing or reraising, so contextlib raises a lifecycle error. The corrected finally block performs cleanup while allowing the original ValueError to reach the caller.
Choose the correction for your caller
If an operation needs retries, put a retry loop around a fresh with statement and the operation itself. Decide whether a failed attempt can be repeated safely before doing so. If the context manager catches an exception only to log it, reraise it; otherwise the with statement may treat it as handled and continue. Keep resource acquisition before the single yield and release in finally. Return normally after yield only when the operation completed or when suppression is explicitly part of the contract.
Limits and the next check
This example uses marker strings rather than real sockets or database transactions. It does not establish that retrying an external side effect is safe. A context manager instance created from this generator is single-use; call the decorated function again to obtain a new instance.
Related: Compare a related data or execution contract.
The embedded Python example was executed as an isolated local script on November 10, 2026 (KST), using Python 3.11.6. It demonstrates the stated boundary; it is not a test of your application or a promise of compatibility with every Python release.