High severity intermediate · Fix: 5-10 min

ConnectionResetError

builtins.ConnectionResetError

What this error means
FastAPI SSE streaming connections drop unexpectedly due to client disconnects or server-side event loop blocking.

Stack trace

traceback
Traceback (most recent call last):
  File "/usr/local/lib/python3.10/site-packages/uvicorn/protocols/http/h11_impl.py", line 429, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "/usr/local/lib/python3.10/site-packages/fastapi/applications.py", line 276, in __call__
    await super().__call__(scope, receive, send)
  File "/usr/local/lib/python3.10/site-packages/starlette/applications.py", line 122, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/usr/local/lib/python3.10/site-packages/starlette/middleware/errors.py", line 184, in __call__
    raise exc
  File "/usr/local/lib/python3.10/site-packages/starlette/middleware/errors.py", line 162, in __call__
    await self.app(scope, receive, _send)
  File "/usr/local/lib/python3.10/site-packages/starlette/middleware/base.py", line 45, in __call__
    await response(scope, receive, send)
  File "/usr/local/lib/python3.10/site-packages/starlette/responses.py", line 338, in __call__
    async for chunk in self.body_iterator:
  File "/app/streaming.py", line 42, in event_generator
    yield f"data: {json.dumps(data)}\n\n"
  File "/usr/local/lib/python3.10/asyncio/streams.py", line 178, in write
    self._transport.write(data)
  File "/usr/local/lib/python3.10/asyncio/selector_events.py", line 103, in write
    self._sock.send(data)
ConnectionResetError: [Errno 104] Connection reset by peer
QUICK FIX
Wrap SSE event generator yields in try/except ConnectionResetError to handle client disconnects gracefully.

Why it happens

FastAPI SSE streams rely on long-lived HTTP connections. If the client closes the connection or network issues occur, the server's attempt to send events raises ConnectionResetError. Additionally, blocking operations in the event loop can delay heartbeats causing client disconnects.

Detection

Monitor server logs for ConnectionResetError during SSE streaming and track client disconnect events. Use middleware or logging to capture dropped connections before they cause failures.

Causes & fixes

1

Client closes the SSE connection unexpectedly (e.g., user navigates away or network drops)

✓ Fix

Catch ConnectionResetError in the event generator and gracefully stop streaming without crashing the server.

2

Blocking synchronous code in the async event loop delays sending keep-alive events

✓ Fix

Refactor blocking code to async or run it in a separate thread/process to keep the event loop responsive.

3

No periodic heartbeat or comment lines sent to keep the SSE connection alive

✓ Fix

Send regular SSE comment lines (e.g., ': keep-alive\n\n') every 15-30 seconds to prevent proxies or clients from closing the connection.

4

Improperly configured server or reverse proxy timeouts close idle SSE connections

✓ Fix

Configure server and proxy timeouts (e.g., nginx proxy_read_timeout) to allow long-lived SSE connections.

Code: broken vs fixed

Broken - triggers the error
python
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import json

app = FastAPI()

async def event_generator():
    data = {"message": "hello"}
    yield f"data: {json.dumps(data)}\n\n"  # This line can raise ConnectionResetError

@app.get("/stream")
async def stream():
    return StreamingResponse(event_generator(), media_type="text/event-stream")
Fixed - works correctly
python
import os
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import json
import asyncio

app = FastAPI()

async def event_generator():
    data = {"message": "hello"}
    try:
        yield f"data: {json.dumps(data)}\n\n"  # Wrapped yield in try/except to handle disconnects
    except ConnectionResetError:
        # Client disconnected, stop streaming gracefully
        return

@app.get("/stream")
async def stream():
    return StreamingResponse(event_generator(), media_type="text/event-stream")

# No hardcoded keys needed here

print("FastAPI SSE streaming server ready")
Added try/except around yield to catch ConnectionResetError when client disconnects, preventing server crash.

Workaround

Wrap the SSE event generator's yield statements in try/except ConnectionResetError blocks and log disconnects to avoid crashing the server.

Prevention

Implement periodic SSE heartbeat comments, avoid blocking code in the event loop, and configure server/proxy timeouts to support long-lived SSE connections.

Python 3.9+ · fastapi >=0.70.0 · tested on 0.95.0
Verified 2026-04
Verify ↗

Community Notes

No notes yetBe the first to share a version-specific fix or tip.