python/cpython · error · RuntimeError

Cannot exit %r without entering first

Error message

Cannot exit %r without entering first

What it means

Raised by warnings.catch_warnings.__exit__ when the context manager's __exit__ is called without a prior __enter__. The object tracks an internal _entered flag set in __enter__ and cleared in __exit__; exiting an un-entered (or already-exited and mutated) instance would corrupt the saved warning-filter state, so CPython guards it with a RuntimeError.

Source

Thrown at Lib/_py_warnings.py:729

            self._module._filters_mutated_lock_held()
            if self._record:
                if _use_context:
                    context.log = log = []
                else:
                    log = []
                    self._module._showwarnmsg_impl = log.append
                # Reset showwarning() to the default implementation to make sure
                # that _showwarnmsg() calls _showwarnmsg_impl()
                self._module.showwarning = self._module._showwarning_orig
            else:
                log = None
        if self._filter is not None:
            self._module.simplefilter(*self._filter)
        return log

    def __exit__(self, *exc_info):
        if not self._entered:
            raise RuntimeError("Cannot exit %r without entering first" % self)
        with _wm._lock:
            if _use_context:
                self._module._warnings_context.set(self._saved_context)
            else:
                self._module.filters = self._filters
                self._module._showwarnmsg_impl = self._showwarnmsg_impl
            self._module.showwarning = self._showwarning
            self._module._filters_mutated_lock_held()


class deprecated:
    """Indicate that a class, function or overload is deprecated.

    When this decorator is applied to an object, the type checker
    will generate a diagnostic on usage of the deprecated object.

    Usage:

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Use the context manager form: with warnings.catch_warnings(): ... and let Python call __exit__ exactly once
  2. If manual control is required, call __enter__ first and guarantee __exit__ runs once (try/finally), and never reuse the instance
  3. Check the internal flag defensively before manual exit: if getattr(cm, '_entered', False): cm.__exit__(...)
  4. Use contextlib.ExitStack.enter_context(cm) instead of hand-managing enter/exit pairing

Example fix

// before
cm = warnings.catch_warnings()
cm.__exit__(None, None, None)  # RuntimeError

# after
with warnings.catch_warnings():
    warnings.simplefilter('ignore')
    do_noisy_thing()
Defensive patterns

Strategy: validation

Validate before calling

import warnings
cm = warnings.catch_warnings()
assert getattr(cm, '_entered', False) is False
cm.__enter__()
try:
    warnings.simplefilter('ignore')
    noisy()
finally:
    if getattr(cm, '_entered', False):
        cm.__exit__(None, None, None)

Try / catch

try:
    cm.__exit__(None, None, None)
except RuntimeError:
    pass  # already exited; state is consistent, safe to ignore

Prevention

When it happens

Trigger: Calling cm = warnings.catch_warnings(); cm.__exit__(None, None, None) directly; manually invoking __enter__/__exit__ (e.g. to emulate a context manager across function boundaries) and calling __exit__ twice; passing a catch_warnings instance to contextlib.ExitStack and popping its callback twice.

Common situations: Code that wraps catch_warnings in a custom try/finally instead of a with block; reusing one catch_warnings instance for nested suppression; refactoring away from 'with' while keeping explicit enter/exit calls; test fixtures that conditionally enter the context.

Related errors


AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14). Data as JSON: /api/errors/58dd3e919fddbc2e. Report an issue: GitHub.