Skip to content

StreamingResponse with async generator sometimes causes socket.send() exception indefinitely #5183

@MichadeGroot

Description

@MichadeGroot

First Check

  • I added a very descriptive title to this issue.
  • I used the GitHub search to find a similar issue and didn't find it.
  • I searched the FastAPI documentation, with the integrated search.
  • I already searched in Google "How to X in FastAPI" and didn't find any information.
  • I already read and followed all the tutorial in the docs and didn't find an answer.
  • I already checked if it is not related to FastAPI but to Pydantic.
  • I already checked if it is not related to FastAPI but to Swagger UI.
  • I already checked if it is not related to FastAPI but to ReDoc.

Commit to Help

  • I commit to help with one of those options 👆

Example Code

import asyncio

import uvicorn
from fastapi import FastAPI
from fastapi.responses import StreamingResponse

app = FastAPI()


def sync_streamer():
    try:
        while True:
            yield b"--boundary\r\nContent-Type: text/plain\r\nContent-Length: 1\r\n\r\n1\r\n"
    except asyncio.CancelledError:
        print("caught cancelled error")


async def async_streamer():
    try:
        while True:
            yield b"--boundary\r\nContent-Type: text/plain\r\nContent-Length: 1\r\n\r\n1\r\n"
    except asyncio.CancelledError:
        print("caught cancelled error")


@app.get("/sync")
async def sync_endpoint():
    return StreamingResponse(
        sync_streamer(),
        media_type="multipart/x-mixed-replace; boundary=boundary",
    )


@app.get("/async")
async def async_endpoint():
    return StreamingResponse(
        async_streamer(),
        media_type="multipart/x-mixed-replace; boundary=boundary",
    )

Description

When I try to send data with a StreamingResponse from an infinite generator I sometimes get an error when the generator is asynchronous. Sometimes it works fine, but sometimes I get an infinite stream of errors: socket.send() raised exception. on the server side.
If I instead use a synchronous generator this problem does not appear at all.

I've based my sample code on #4146 and the CancelledError does get caught sometimes, but sometimes it doesn't. Right now I can avoid this issue by making the generator synchronous, but it would be nice to have the option to use async generators as well.

It could be that this is a Starlette issue but I am really not sure.

Operating System

Linux

Operating System Details

No response

FastAPI Version

0.79.0

Python Version

3.8.10

Additional Context

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions