Skip to content

stream_with_context does not work with async routes #5774

@nkouevda-patreon

Description

@nkouevda-patreon

Consider this trivial route + test in app.py:

import flask
from flask import Flask
from flask import Response

import pytest

app = Flask(__name__)


@app.route("/foo")
async def foo():
    def gen():
        yield "bar"

    return Response(flask.stream_with_context(gen()))


def test_foo():
    with app.test_client() as client:
        client.get("/foo")

With the following requirements.txt:

flask[async]==3.1.1
pytest==8.4.1
werkzeug==3.1.3

Running pytest app.py results in:

============================= test session starts ==============================
platform darwin -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0
rootdir: /private/tmp/stream_with_context
collected 1 item

app.py F                                                                 [100%]

=================================== FAILURES ===================================
___________________________________ test_foo ___________________________________

    def test_foo():
        with app.test_client() as client:
>           client.get("/foo")

app.py:20:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
venv/lib/python3.12/site-packages/werkzeug/test.py:1162: in get
    return self.open(*args, **kw)
           ^^^^^^^^^^^^^^^^^^^^^^
venv/lib/python3.12/site-packages/flask/testing.py:235: in open
    response = super().open(
venv/lib/python3.12/site-packages/werkzeug/test.py:1116: in open
    response_parts = self.run_wsgi_app(request.environ, buffered=buffered)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
venv/lib/python3.12/site-packages/werkzeug/test.py:988: in run_wsgi_app
    rv = run_wsgi_app(self.application, environ, buffered=buffered)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
venv/lib/python3.12/site-packages/werkzeug/test.py:1264: in run_wsgi_app
    app_rv = app(environ, start_response)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
venv/lib/python3.12/site-packages/flask/app.py:1536: in __call__
    return self.wsgi_app(environ, start_response)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
venv/lib/python3.12/site-packages/flask/app.py:1527: in wsgi_app
    ctx.pop(error)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <RequestContext 'http://localhost/foo' [GET] of app>, exc = None

    def pop(self, exc: BaseException | None = _sentinel) -> None:  # type: ignore
        """Pops the request context and unbinds it by doing that.  This will
        also trigger the execution of functions registered by the
        :meth:`~flask.Flask.teardown_request` decorator.

        .. versionchanged:: 0.9
           Added the `exc` argument.
        """
        clear_request = len(self._cv_tokens) == 1

        try:
            if clear_request:
                if exc is _sentinel:
                    exc = sys.exc_info()[1]
                self.app.do_teardown_request(exc)

                request_close = getattr(self.request, "close", None)
                if request_close is not None:
                    request_close()
        finally:
            ctx = _cv_request.get()
            token, app_ctx = self._cv_tokens.pop()
>           _cv_request.reset(token)
E           ValueError: <Token var=<ContextVar name='flask.request_ctx' at 0x103cd9a30> at 0x104b23a40> was created in a different Context

venv/lib/python3.12/site-packages/flask/ctx.py:418: ValueError
=========================== short test summary info ============================
FAILED app.py::test_foo - ValueError: <Token var=<ContextVar name='flask.request_ctx' at 0x103cd9a30>...
============================== 1 failed in 0.11s ===============================

Similarly, python -m flask run and then curl http://127.0.0.1:5000/foo results in:

127.0.0.1 - - [15/Jul/2025 13:40:14] "GET /foo HTTP/1.1" 500 -
Error on request:
Traceback (most recent call last):
  File "/private/tmp/stream_with_context/venv/lib/python3.12/site-packages/werkzeug/serving.py", line 370, in run_wsgi
    execute(self.server.app)
  File "/private/tmp/stream_with_context/venv/lib/python3.12/site-packages/werkzeug/serving.py", line 331, in execute
    application_iter = app(environ, start_response)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/private/tmp/stream_with_context/venv/lib/python3.12/site-packages/flask/app.py", line 1536, in __call__
    return self.wsgi_app(environ, start_response)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/private/tmp/stream_with_context/venv/lib/python3.12/site-packages/flask/app.py", line 1527, in wsgi_app
    ctx.pop(error)
  File "/private/tmp/stream_with_context/venv/lib/python3.12/site-packages/flask/ctx.py", line 418, in pop
    _cv_request.reset(token)
ValueError: <Token var=<ContextVar name='flask.request_ctx' at 0x1029ccea0> at 0x1049e1980> was created in a different Context

If the route is defined def foo(): without async, then this works without issue.

Or if we remove stream_with_context and just return Response(gen()), also works.

But the combination of async + stream_with_context fails.

There's also no issue with e.g. flask 2.1.0; I believe this was introduced in flask 2.2.0, specifically by #4682.

Environment:

  • Python version: 3.12.11
  • Flask version: 3.1.1

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions