{"id":"6ca40e4edafdd9a4","repo":"pytest-dev/pytest","slug":"cannot-exit-self-r-without-entering-first","errorCode":null,"errorMessage":"Cannot exit {self!r} without entering first","messagePattern":"Cannot exit (.+?) without entering first","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"src/_pytest/recwarn.py","lineNumber":255,"sourceCode":"        if self._entered:\n            __tracebackhide__ = True\n            raise RuntimeError(f\"Cannot enter {self!r} twice\")\n        _list = super().__enter__()\n        # record=True means it's None.\n        assert _list is not None\n        self._list = _list\n        warnings.simplefilter(\"always\")\n        return self\n\n    def __exit__(\n        self,\n        exc_type: type[BaseException] | None,\n        exc_val: BaseException | None,\n        exc_tb: TracebackType | None,\n    ) -> None:\n        if not self._entered:\n            __tracebackhide__ = True\n            raise RuntimeError(f\"Cannot exit {self!r} without entering first\")\n\n        super().__exit__(exc_type, exc_val, exc_tb)\n\n        # Built-in catch_warnings does not reset entered state so we do it\n        # manually here for this context manager to become reusable.\n        self._entered = False\n\n\n@final\nclass WarningsChecker(WarningsRecorder):\n    def __init__(\n        self,\n        expected_warning: type[Warning] | tuple[type[Warning], ...] = Warning,\n        match_expr: str | re.Pattern[str] | None = None,\n        *,\n        _ispytest: bool = False,\n    ) -> None:\n        check_ispytest(_ispytest)","sourceCodeStart":237,"sourceCodeEnd":273,"githubUrl":"https://github.com/pytest-dev/pytest/blob/98b357f69e380da908740a212288d73b2ee06687/src/_pytest/recwarn.py#L237-L273","documentation":"WarningsRecorder.__exit__ raises RuntimeError if called on an instance that was not entered first. The recorder tracks entered state and rejects unbalanced exit.","triggerScenarios":"Manually calling recorder.__exit__(...) without a prior __enter__, or a code path that exits twice (the second after state was reset). Hits recwarn.py:255.","commonSituations":"Direct manipulation of the context-manager protocol; a cleanup/finally block that exits a recorder that was never entered due to an earlier error.","solutions":["Always use the `with` statement so enter/exit are balanced automatically.","If managing enter/exit manually, guard exit with a check of the entered state.","Ensure __enter__ succeeds before any code path can reach __exit__."],"exampleFix":"// before\nrecorder.__exit__(None, None, None)  # never entered\n// after\nwith recorder:\n    ...","handlingStrategy":"validation","validationCode":"if not getattr(recorder, '_entered', False):\n    raise RuntimeError('recorder not entered; cannot exit')\nrecorder.__exit__(None, None, None)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always use the `with` statement for balanced enter/exit.","Never call __exit__ manually without a successful __enter__.","Guard manual cleanup with an entered-state check."],"tags":["pytest","warns","context-manager","runtime"],"analyzedSha":"98b357f69e380da908740a212288d73b2ee06687","analyzedAt":"2026-08-04T20:26:34.442Z","schemaVersion":2}