pytest-dev/pytest · error · BaseExceptionGroup

errors while tearing down fixture

Error message

errors while tearing down fixture "{self.argname}" of {node}

What it means

At fixtures.py:1230 pytest raises BaseExceptionGroup('errors while tearing down fixture ...') when TWO OR MORE exceptions occur while running a fixture's registered finalizers. Single exceptions are re-raised as-is; multiple are grouped (reversed to reflect execution order) so teardown failures are not hidden. The fixture's cached_result and finalizers are cleared before raising.

Solutions

  1. Inspect the BaseExceptionGroup.subgroups/exceptions to find each root cause; fix the most fundamental one first.
  2. Make each teardown step defensive (try/except inside the finalizer) so one failure does not cascade.
  3. Use context managers / addfinalizer per-resource to isolate teardown scope.
  4. Reproduce with -p no:randomly and a single test to reduce concurrency-induced multi-failures.

Example fix

// before
@pytest.fixture
def svc():
    yield start()
    stop_db()   # may raise
    stop_cache()# also raises -> group
// after
@pytest.fixture
def svc():
    yield start()
    for closer in (stop_db, stop_cache):
        try:
            closer()
        except Exception as e:
            logging.warning('teardown failed: %s', e)
Defensive patterns

Strategy: try-catch

Try / catch

# In conftest.py, make teardown resilient
import pytest, logging
@pytest.fixture(autouse=True)
def _safe_teardown():
    yield
    # user teardown handled per-fixture below

Prevention

When it happens

Trigger: A yield-fixture whose post-yield teardown and one or more addfinalizer callbacks all raise; multiple dependencies of a fixture fail teardown simultaneously; teardown code calling external resources (DB close, file unlock) that error.

Common situations: Cleanup code hitting already-closed resources; finalizers that depend on side effects from other failing finalizers; test environments where multiple external services are torn down and several are unreachable.

Related errors


AI-assisted analysis of pytest-dev/pytest@0d6fbdeffa (2026-08-11). Data as JSON: /api/errors/5bd26589b2af6b77. Report an issue: GitHub.

Appendix: source

Thrown at src/_pytest/fixtures.py:1230

        exceptions: list[BaseException] = []
        while self._finalizers:
            fin = self._finalizers.pop()
            try:
                fin()
            except BaseException as e:
                exceptions.append(e)
        node = request.node
        # Even if finalization fails, we invalidate the cached fixture
        # value and remove all finalizers because they may be bound methods
        # which will keep instances alive.
        self.cached_result = None
        self._finalizers.clear()
        if len(exceptions) == 1:
            raise exceptions[0]
        elif len(exceptions) > 1:
            msg = f'errors while tearing down fixture "{self.argname}" of {node}'
            raise BaseExceptionGroup(msg, exceptions[::-1])

    def execute(self, request: SubRequest) -> FixtureValue:
        """Return the value of this fixture, executing it if not cached."""
        # Ensure that the dependent fixtures requested by this fixture are loaded.
        # This needs to be done before checking if we have a cached value, since
        # if a dependent fixture has their cache invalidated, e.g. due to
        # parametrization, they finalize themselves and fixtures depending on it
        # (which will likely include this fixture) setting `self.cached_result = None`.
        # See #4871
        requested_fixtures_that_should_finalize_us = []
        for argname in self.argnames:
            fixturedef = request._get_active_fixturedef(argname)
            # Saves requested fixtures in a list so we later can add our finalizer
            # to them, ensuring that if a requested fixture gets torn down we get torn
            # down first. This is generally handled by SetupState, but still currently
            # needed when this fixture is not parametrized but depends on a parametrized
            # fixture.
            requested_fixtures_that_should_finalize_us.append(fixturedef)

View on GitHub (pinned to 0d6fbdeffa)