pallets/flask · error · RuntimeError

'after_this_request' can only be used when a request context

Error message

'after_this_request' can only be used when a request context is active, such as in a view function.

What it means

`after_this_request` registers a callback to run after only the current request, appending it to the active context's `_after_request_functions` (src/flask/ctx.py:139-148). It checks `_cv_app.get(None)` and requires the context to both exist and have a request (`ctx.has_request`); otherwise there is no 'this request' to attach the callback to, so it raises this RuntimeError. Note that in Flask 3.x an app context without a request also fails this check.

Source

Thrown at src/flask/ctx.py:142

    request context, rather than during setup.

    .. code-block:: python

        @app.route("/")
        def index():
            @after_this_request
            def add_header(response):
                response.headers["X-Foo"] = "Parachute"
                return response

            return "Hello, World!"

    .. versionadded:: 0.9
    """
    ctx = _cv_app.get(None)

    if ctx is None or not ctx.has_request:
        raise RuntimeError(
            "'after_this_request' can only be used when a request"
            " context is active, such as in a view function."
        )

    ctx._after_request_functions.append(f)
    return f


F = t.TypeVar("F", bound=t.Callable[..., t.Any])


def copy_current_request_context(f: F) -> F:
    """Decorate a function to run inside the current request context. This can
    be used when starting a background task, otherwise it will not see the app
    and request objects that were active in the parent.

    .. warning::

View on GitHub ↗ (pinned to 6a2f545bfd)

Solutions

  1. Move the `after_this_request` registration inside a view function or a before-request hook, where a request context is active.
  2. If the behavior should apply to every request, use `@app.after_request` at setup time instead.
  3. In tests, wrap the call in `with app.test_request_context(): ...`.
  4. For background work, don't register per-request callbacks — operate on the response in the view before spawning the task.

Example fix

# before
@after_this_request  # module level -> RuntimeError
def add_header(response):
    response.headers['X-Foo'] = 'Parachute'
    return response

# after
@app.route('/')
def index():
    @after_this_request
    def add_header(response):
        response.headers['X-Foo'] = 'Parachute'
        return response
    return 'Hello'
Defensive patterns

Strategy: type-guard

Validate before calling

from flask import has_request_context, after_this_request

if has_request_context():
    @after_this_request
    def add_header(response):
        response.headers["X-Processed"] = "1"
        return response

Type guard

from flask import has_request_context

def can_register_after_request() -> bool:
    return has_request_context()

Try / catch

try:
    after_this_request(cleanup)
except RuntimeError:
    # Not in a request — run the cleanup directly instead
    cleanup(None)

Prevention

When it happens

Trigger: Calling `after_this_request(fn)` (or using it as a decorator) outside request dispatch: at import time, in `before_first_request`-style setup code, inside a plain `with app.app_context()` block, in a CLI command, or in a background thread/task where the request ContextVar isn't propagated.

Common situations: Applying `@after_this_request` at module level or inside app setup instead of inside a view function; using it in Celery tasks or threads spawned from a view; unit-testing a helper that registers the callback without `app.test_request_context()`; trying to set response cookies from non-request code.

Related errors


AI-assisted analysis of pallets/flask@6a2f545bfd (2026-07-31). Data as JSON: /data/errors/bc8a3c7c21374fcf.json. Report an issue: GitHub ↗.