pytest-dev/pytest · error · BaseExceptionGroup

errors during test teardown

Error message

errors during test teardown

What it means

Raised as a BaseExceptionGroup by SetupState.teardown_exact when more than one exception is collected while popping and running finalizers from the fixture/test stack at the end of a test. pytest groups the per-node teardown failures into a single exception group so teardown errors from sibling fixtures are not silently lost. Each subgroup's message is 'errors while tearing down <node>'.

Source

Thrown at src/_pytest/runner.py:581

            node, (finalizers, _) = self.stack.popitem()
            these_exceptions = []
            while finalizers:
                fin = finalizers.pop()
                try:
                    fin()
                except TEST_OUTCOME as e:
                    these_exceptions.append(e)

            if len(these_exceptions) == 1:
                exceptions.extend(these_exceptions)
            elif these_exceptions:
                msg = f"errors while tearing down {node!r}"
                exceptions.append(BaseExceptionGroup(msg, these_exceptions[::-1]))

        if len(exceptions) == 1:
            raise exceptions[0]
        elif exceptions:
            raise BaseExceptionGroup("errors during test teardown", exceptions[::-1])
        if nextitem is None:
            assert not self.stack


def collect_one_node(collector: Collector) -> CollectReport:
    ihook = collector.ihook
    ihook.pytest_collectstart(collector=collector)
    rep: CollectReport = ihook.pytest_make_collect_report(collector=collector)
    call = rep.__dict__.pop("call", None)
    if call and check_interactive_exception(call, rep):
        ihook.pytest_exception_interact(node=collector, call=call, report=rep)
    return rep

View on GitHub (pinned to 98b357f69e)

Solutions

  1. Inspect the ExceptionGroup's sub-exceptions (use `except*` on Python 3.11+, or `e.exceptions`) to identify each root cause individually.
  2. Make each fixture teardown defensive: wrap cleanup bodies in try/except so one failing finalizer cannot mask others or cascade.
  3. Reproduce teardown in isolation by running only the failing test with `-p no:randomly` and `--setup-show` to see fixture order.
  4. Ensure resource handles (files, sockets, subprocesses) are closed in finally blocks rather than bare yield fixtures.

Example fix

// before
@pytest.fixture
def db():
    conn = open_conn()
    yield conn
    conn.close()  # raises if conn is broken, masks other teardown errors

// after
@pytest.fixture
def db():
    conn = open_conn()
    try:
        yield conn
    finally:
        try:
            conn.close()
        except Exception:
            pass  # log and continue; let other finalizers run
Defensive patterns

Strategy: try-catch

Try / catch

# Python 3.11+
try:
    run_test(item)
except* Exception as eg:
    for e in eg.exceptions:
        log_teardown_failure(e)

# Python < 3.11
try:
    run_test(item)
except BaseExceptionGroup as eg:
    for e in eg.exceptions:
        log_teardown_failure(e)

Prevention

When it happens

Trigger: Two or more fixture finalizers (yield fixtures' post-yield code, addfinalizer callbacks, autouse fixture teardowns) raise during teardown of the same test item, or finalizers across different nodes in the stack all fail. TEST_OUTCOME exceptions (AssertionError, Exception, Skip, etc.) are captured and bundled here.

Common situations: A fixture's cleanup hits an assertion, while a dependent fixture also fails to close a resource; switching from a passing to a failing test where multiple teardowns now misbehave; plugin finalizers interacting with broken state left by the failing test body.

Related errors


AI-assisted analysis of pytest-dev/pytest@98b357f69e (2026-08-04). Data as JSON: /data/errors/1bf611b3124e735a.json. Report an issue: GitHub.