pytest-dev/pytest · error · RuntimeError
Cannot exit without entering first
Error message
Cannot exit {self!r} without entering first What it means
Raised by WarningsRecorder.__exit__ when exit is called before enter. The recorder requires its setup (catch_warnings.__enter__) to have run to restore state cleanly; exiting without entering would leave the warnings module in an inconsistent state. The _entered guard detects the missing setup and raises RuntimeError.
Solutions
- Always use the with statement so __enter__/__exit__ stay paired.
- If managing manually, set a flag and only call __exit__ when __enter__ succeeded.
- Create a fresh recorder per use to avoid stale _entered state.
Example fix
// before
recorder = WarningsRecorder(_ispytest=True)
try:
...
finally:
recorder.__exit__(None, None, None) # RuntimeError
// after
with WarningsRecorder(_ispytest=True) as recorder:
... Defensive patterns
Strategy: validation
Validate before calling
assert getattr(recorder, '_entered', False), f'cannot exit {recorder!r} before entering' Type guard
def recorder_exitable(recorder) -> bool:
return getattr(recorder, '_entered', False) Prevention
- Always use the with statement so __enter__ precedes __exit__.
- In try/finally, guard __exit__ with a flag set only after __enter__ succeeds.
- Create a fresh recorder per use to avoid stale lifecycle state.
When it happens
Trigger: Calling recorder.__exit__(None, None, None) without a prior __enter__; a try/finally that calls __exit__ on an un-entered recorder; miswired custom context managers that forward __exit__ unconditionally.
Common situations: Exception in setup code that skips __enter__ but a finally block still calls __exit__; reuse of a closed recorder; buggy fixtures.
Related errors
- Cannot enter twice
- not found in warning list
- exceptions must be derived from Warning, not
- object (type: ) must be callable
- Unexpected keyword arguments passed to pytest.warns
AI-assisted analysis of pytest-dev/pytest@0d6fbdeffa (2026-08-11).
Data as JSON: /api/errors/6ca40e4edafdd9a4.
Report an issue: GitHub.
Appendix: source
Thrown at src/_pytest/recwarn.py:255
if self._entered:
__tracebackhide__ = True
raise RuntimeError(f"Cannot enter {self!r} twice")
_list = super().__enter__()
# record=True means it's None.
assert _list is not None
self._list = _list
warnings.simplefilter("always")
return self
def __exit__(
self,
exc_type: type[BaseException] | None,
exc_val: BaseException | None,
exc_tb: TracebackType | None,
) -> None:
if not self._entered:
__tracebackhide__ = True
raise RuntimeError(f"Cannot exit {self!r} without entering first")
super().__exit__(exc_type, exc_val, exc_tb)
# Built-in catch_warnings does not reset entered state so we do it
# manually here for this context manager to become reusable.
self._entered = False
@final
class WarningsChecker(WarningsRecorder):
def __init__(
self,
expected_warning: type[Warning] | tuple[type[Warning], ...] = Warning,
match_expr: str | re.Pattern[str] | None = None,
*,
_ispytest: bool = False,
) -> None:
check_ispytest(_ispytest)View on GitHub (pinned to 0d6fbdeffa)