Internal Error Collection Mechanisms
Flask ensures that application cleanup logic—such as closing database connections or clearing session data—executes reliably even if individual cleanup tasks fail. This is achieved through a private internal mechanism centered around the _CollectErrors context manager.
The Error Collection Context Manager
The core of this mechanism is the _CollectErrors class located in src/flask/helpers.py. It is designed to wrap multiple operations, capturing any exceptions they raise without stopping the execution of subsequent operations.
class _CollectErrors:
"""A context manager that records and silences an error raised within it.
Used to run all teardown functions, then raise any errors afterward.
"""
def __init__(self) -> None:
self.errors: list[BaseException] = []
def __enter__(self) -> None:
pass
def __exit__(
self,
exc_type: type[BaseException] | None,
exc_val: BaseException | None,
exc_tb: TracebackType | None,
) -> bool:
if exc_val is not None:
self.errors.append(exc_val)
return True
def raise_any(self, message: str) -> None:
"""Raise if any errors were collected."""
if self.errors:
if sys.version_info >= (3, 11):
raise BaseExceptionGroup(message, self.errors)
else:
raise self.errors[0]
The __exit__ method always returns True, which tells Python to suppress the exception and continue execution. The collected exceptions are stored in the errors list and can be raised later using raise_any().
Teardown Orchestration
Flask uses _CollectErrors in three primary locations to manage the teardown lifecycle of requests and application contexts.
Request Teardown
In Flask.do_teardown_request (found in src/flask/app.py), Flask iterates through all registered teardown functions. Each function call is wrapped in the same collect_errors instance. This ensures that if one blueprint's teardown function fails, other blueprints and the global teardown functions still run.
def do_teardown_request(
self, ctx: RequestContext, exc: BaseException | None = None
) -> None:
collect_errors = _CollectErrors()
for name in chain(ctx.request.blueprints, (None,)):
if name in self.teardown_request_funcs:
for func in reversed(self.teardown_request_funcs[name]):
with collect_errors:
self.ensure_sync(func)(exc)
with collect_errors:
request_tearing_down.send(self, _async_wrapper=self.ensure_sync, exc=exc)
collect_errors.raise_any("Errors during request teardown")
Application Context Teardown
Similarly, Flask.do_teardown_appcontext uses the mechanism to execute functions decorated with @app.teardown_appcontext.
Context Popping
The highest level of orchestration occurs in RequestContext.pop within src/flask/ctx.py. When a request context is popped (usually at the end of a WSGI request), Flask must perform several cleanup steps in order:
- Call
do_teardown_request. - Close the request object.
- Call
do_teardown_appcontext. - Signal that the app context was popped.
By wrapping each of these major steps in _CollectErrors, Flask guarantees that an error in request teardown does not prevent the application context from being cleaned up.
Version-Specific Exception Handling
The behavior of raise_any() depends on the Python version:
- Python 3.11+: Flask raises a
BaseExceptionGroup. This allows developers to catch and inspect every single error that occurred during the teardown process. - Older Python versions: Flask re-raises only the first exception that was captured. While subsequent cleanup functions still execute, only the first failure is reported to the caller.
Robust Teardown Example
The following pattern, adapted from tests/test_appctx.py, demonstrates how Flask handles multiple failures during teardown. Even with multiple functions raising ValueError, every handler is executed.
@app.teardown_request
def fail_request(e):
raise ValueError("request_teardown failed")
@app.teardown_appcontext
def fail_app(e):
raise ValueError("app_teardown failed")
# When the request finishes:
# 1. fail_request runs and its error is collected.
# 2. fail_app runs and its error is collected.
# 3. On Python 3.11+, an ExceptionGroup is raised containing both ValueErrors.
This internal mechanism is critical for resource management, ensuring that even in failing states, the application attempts to release file handles, database connections, and other vital resources.