pytest-dev/pytest · error · BaseExceptionGroup
errors while tearing down fixture "{self.argname}" of {node}
Error message
errors while tearing down fixture "{self.argname}" of {node} What it means
Raised by `FixtureDef.finish` when two or more finalizers/teardown steps of a single fixture raise during teardown. pytest collects all exceptions and re-raises them wrapped in a `BaseExceptionGroup` (the message names the fixture argname and the node) so no teardown error is swallowed. A single exception is re-raised unwrapped.
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 98b357f69e)
Solutions
- Make teardown code defensive: wrap each cleanup step in try/except and log instead of raising where safe.
- Use `except*` (exception groups) at the test runner level, or `pytest.raises(BaseExceptionGroup)` in tests.
- Identify which finalizers failed from the message and fix the root cause of each.
Example fix
// before
@pytest.fixture
def db():
conn = connect()
yield conn
conn.close() # may raise
conn.log_logout() # also may raise -> group
// after
@pytest.fixture
def db():
conn = connect()
yield conn
try:
conn.close()
finally:
try:
conn.log_logout()
except Exception:
pass Defensive patterns
Strategy: try-catch
Try / catch
try:
yield resource
except BaseExceptionGroup as eg:
for e in eg.exceptions:
log.warning("teardown error: %r", e) Prevention
- Wrap each teardown step in try/except so one failure does not cascade.
- Register finalizers that clean independent resources so partial failures are tolerated.
- Test teardown paths explicitly to surface latent errors.
When it happens
Trigger: A fixture whose teardown (after yield) plus one or more of its dependent finalizers both raise. Common when the post-yield cleanup raises and an `addfinalizer` callback registered elsewhere also raises.
Common situations: Teardown of a resource fails (connection already closed) and a second finalizer also fails. CI flakiness where cleanup hits a transient error. Migrating to Python 3.11+ where ExceptionGroup surfaces.
Related errors
- The fixture value for "{argname}" is not available during te
- errors during test teardown
- function not available in {self.scope}-scoped context
- cls not available in {self.scope}-scoped context
- module not available in {self.scope}-scoped context
AI-assisted analysis of pytest-dev/pytest@98b357f69e (2026-08-04).
Data as JSON: /data/errors/5bd26589b2af6b77.json.
Report an issue: GitHub.