Skip to content

📝 Add await in StreamingResponse code example to allow cancellation#14681

Merged
tiangolo merged 3 commits intofastapi:masterfrom
casperdcl:patch-1
Feb 27, 2026
Merged

📝 Add await in StreamingResponse code example to allow cancellation#14681
tiangolo merged 3 commits intofastapi:masterfrom
casperdcl:patch-1

Conversation

@casperdcl
Copy link
Contributor

@casperdcl casperdcl commented Jan 10, 2026

Fixes #14680

async generators passed to starlette.StreamingResponse must contain an await command1 otherwise they block

Footnotes

  1. https://github.com/Kludex/starlette/discussions/1776#discussioncomment-3207518

@github-actions github-actions bot added the docs Documentation about how to use FastAPI label Jan 10, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Jan 10, 2026

@codspeed-hq
Copy link

codspeed-hq bot commented Jan 10, 2026

Merging this PR will not alter performance

✅ 20 untouched benchmarks


Comparing casperdcl:patch-1 (163e35c) with master (749cefd)1

Open in CodSpeed

Footnotes

  1. No successful run was found on master (ea80e46) during the generation of this report, so 749cefd was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@Ayu456sh

This comment was marked as spam.

akash1551 added a commit to akash1551/fastapi that referenced this pull request Feb 6, 2026
Body:
[`StreamingResponse` docs](https://fastapi.tiangolo.com/advanced/custom-response/?h=responses#streamingresponse) state:

> Takes an async generator or a normal generator/iterator

However based on Kludex/starlette#1776 (comment) `async` generetors need something `await`ed to work:

```py
import asyncio
from time import sleep
from fastapi import FastAPI
from fastapi.responses import StreamingResponse

app = FastAPI()

async def fake_video_streamer():
    for i in range(10):
        sleep(0.1)
        yield b"some fake video bytes"
        # await asyncio.sleep(0)  # <-- uncomment to fix example

@app.get("/")
async def main():
    """
    this is as per
    https://fastapi.tiangolo.com/advanced/custom-response/?h=responses#streamingresponse
    but does not to work (blocks for 1 sec, then returns all chunks at once):
      curl -sNo- localhost:8000
      wget -qO- localhost:8000
    """
    return StreamingResponse(fake_video_streamer())
```

- `uvicorn==0.40.0`
- `fastapi==0.128.0`

--- Comments ---

User: casperdcl
It seems based on Kludex/starlette#1776 (comment) that `await asyncio.sleep(0)` needs to be added to the documented example to work.

User: mirza-mohibul-hasan
Hi @casperdcl, thanks for raising this. I understand the issue and the root cause with async generators blocking without an await. If the fix isn’t already finalized, I’d be happy to work on this or help refine the docs/validate the example. Please let me know if that works for you.

User: casperdcl
fixed in fastapi#14681
akash1551 added a commit to akash1551/fastapi that referenced this pull request Feb 6, 2026
Body:
[`StreamingResponse` docs](https://fastapi.tiangolo.com/advanced/custom-response/?h=responses#streamingresponse) state:

> Takes an async generator or a normal generator/iterator

However based on Kludex/starlette#1776 (comment) `async` generetors need something `await`ed to work:

```py
import asyncio
from time import sleep
from fastapi import FastAPI
from fastapi.responses import StreamingResponse

app = FastAPI()

async def fake_video_streamer():
    for i in range(10):
        sleep(0.1)
        yield b"some fake video bytes"
        # await asyncio.sleep(0)  # <-- uncomment to fix example

@app.get("/")
async def main():
    """
    this is as per
    https://fastapi.tiangolo.com/advanced/custom-response/?h=responses#streamingresponse
    but does not to work (blocks for 1 sec, then returns all chunks at once):
      curl -sNo- localhost:8000
      wget -qO- localhost:8000
    """
    return StreamingResponse(fake_video_streamer())
```

- `uvicorn==0.40.0`
- `fastapi==0.128.0`

--- Comments ---

User: casperdcl
It seems based on Kludex/starlette#1776 (comment) that `await asyncio.sleep(0)` needs to be added to the documented example to work.

User: mirza-mohibul-hasan
Hi @casperdcl, thanks for raising this. I understand the issue and the root cause with async generators blocking without an await. If the fix isn’t already finalized, I’d be happy to work on this or help refine the docs/validate the example. Please let me know if that works for you.

User: casperdcl
fixed in fastapi#14681
akash1551 added a commit to akash1551/fastapi that referenced this pull request Feb 6, 2026
Body:
[`StreamingResponse` docs](https://fastapi.tiangolo.com/advanced/custom-response/?h=responses#streamingresponse) state:

> Takes an async generator or a normal generator/iterator

However based on Kludex/starlette#1776 (comment) `async` generetors need something `await`ed to work:

```py
import asyncio
from time import sleep
from fastapi import FastAPI
from fastapi.responses import StreamingResponse

app = FastAPI()

async def fake_video_streamer():
    for i in range(10):
        sleep(0.1)
        yield b"some fake video bytes"
        # await asyncio.sleep(0)  # <-- uncomment to fix example

@app.get("/")
async def main():
    """
    this is as per
    https://fastapi.tiangolo.com/advanced/custom-response/?h=responses#streamingresponse
    but does not to work (blocks for 1 sec, then returns all chunks at once):
      curl -sNo- localhost:8000
      wget -qO- localhost:8000
    """
    return StreamingResponse(fake_video_streamer())
```

- `uvicorn==0.40.0`
- `fastapi==0.128.0`

--- Comments ---

User: casperdcl
It seems based on Kludex/starlette#1776 (comment) that `await asyncio.sleep(0)` needs to be added to the documented example to work.

User: mirza-mohibul-hasan
Hi @casperdcl, thanks for raising this. I understand the issue and the root cause with async generators blocking without an await. If the fix isn’t already finalized, I’d be happy to work on this or help refine the docs/validate the example. Please let me know if that works for you.

User: casperdcl
fixed in fastapi#14681
@alejsdev alejsdev changed the title fix StreamingResponse example 📝 Fix StreamingResponse example Feb 10, 2026
@github-actions github-actions bot added the conflicts Automatically generated when a PR has a merge conflict label Feb 12, 2026
@github-actions
Copy link
Contributor

This pull request has a merge conflict that needs to be resolved.

Copy link
Member

@YuriiMotov YuriiMotov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@casperdcl, thanks!

I think we should add a note about this right after the code example.

Something like:


/// note

Note that we added `await asyncio.sleep(0)` inside the generator function.

This generator does not have any other `await` inside the loop. In asyncio, a task can only be cancelled when it reaches an `await`.

If there is no `await`, the generator can not be cancelled properly and may keep running even after cancellation is requested.

Adding `await asyncio.sleep(0)` gives the event loop a chance to handle cancellation.

///

What do you think?

@github-actions github-actions bot removed the conflicts Automatically generated when a PR has a merge conflict label Feb 19, 2026
@casperdcl
Copy link
Contributor Author

rebased & added note :)

@YuriiMotov
Copy link
Member

You shouldn't update translations. Please, only update English version

@YuriiMotov YuriiMotov changed the title 📝 Fix StreamingResponse example 📝 Fix StreamingResponse code example Feb 19, 2026
Copy link
Member

@YuriiMotov YuriiMotov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@casperdcl, thank you!
Passing this to Sebastian for the final review

See also alternative wording: #14993

@Areeb455
Copy link

Hey @casperdcl, I reviewed and tested this PR locally.

Testing Results:

  • Ran the updated StreamingResponse example from docs_src.
  • Verified that the stream initializes correctly and handles the generator as expected.
  • Checked the doc alignment with the latest version of Starlette/FastAPI.

The proposed fix correctly addresses the issue and makes the tutorial much more reliable for users. LGTM!

@tiangolo tiangolo changed the title 📝 Fix StreamingResponse code example 📝 Add await in StreamingResponse code example to allow cancellation Feb 27, 2026
Copy link
Member

@tiangolo tiangolo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! 🚀

I also had that in mind when building https://fastapi.tiangolo.com/tutorial/stream-json-lines/ and https://fastapi.tiangolo.com/advanced/stream-data/ , so that it's handled automatically with that new style. (about to be released in the next hours).

@tiangolo tiangolo enabled auto-merge (squash) February 27, 2026 19:11
@tiangolo tiangolo merged commit c3f54a0 into fastapi:master Feb 27, 2026
37 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Documentation about how to use FastAPI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

StreamingResponse doc example needs correction

5 participants