ConnectionResetError
builtins.ConnectionResetError
Stack trace
Traceback (most recent call last):
File "/app/main.py", line 45, in event_generator
yield f"data: {message}\n\n"
File "/usr/local/lib/python3.9/site-packages/starlette/responses.py", line 123, in __call__
await send({"type": "http.response.body", "body": chunk.encode(), "more_body": True})
File "/usr/local/lib/python3.9/asyncio/streams.py", line 541, in write
self._transport.write(data)
File "/usr/local/lib/python3.9/asyncio/selector_events.py", line 103, in write
raise ConnectionResetError('Cannot write to closing transport')
builtins.ConnectionResetError: Cannot write to closing transport Why it happens
SSE connections rely on a persistent HTTP connection to stream events. If the client disconnects abruptly or the server tries to write to a closed connection, a ConnectionResetError occurs. Network interruptions, client timeouts, or improper server-side handling of disconnects cause this error.
Detection
Monitor server logs for ConnectionResetError during SSE streaming and implement exception handling around the event generator to detect client disconnects gracefully.
Causes & fixes
Client closes the SSE connection unexpectedly (e.g., browser tab closed or network lost)
Catch ConnectionResetError in the event generator and stop yielding events to cleanly close the connection.
Server attempts to write to the SSE stream after the client has disconnected
Add try/except around yield/send calls in the SSE generator to handle ConnectionResetError and terminate the generator.
No heartbeat or keep-alive messages causing proxies or clients to drop the connection
Implement periodic heartbeat events (e.g., sending a comment line ': keep-alive\n\n') to keep the connection alive.
Code: broken vs fixed
from fastapi import FastAPI
from starlette.responses import EventSourceResponse
app = FastAPI()
async def event_generator():
while True:
message = "Hello"
yield f"data: {message}\n\n" # This line raises ConnectionResetError on client disconnect
@app.get("/sse")
async def sse_endpoint():
return EventSourceResponse(event_generator()) import os
from fastapi import FastAPI
from starlette.responses import EventSourceResponse
app = FastAPI()
async def event_generator():
try:
while True:
message = "Hello"
yield f"data: {message}\n\n" # Wrapped in try/except to handle disconnects
except ConnectionResetError:
print("Client disconnected, stopping event generator")
return
@app.get("/sse")
async def sse_endpoint():
return EventSourceResponse(event_generator()) # Fixed: added disconnect handling Workaround
Wrap the SSE generator yield calls in try/except ConnectionResetError and silently stop yielding events when the client disconnects to avoid crashes.
Prevention
Implement heartbeat messages in the SSE stream and robustly handle client disconnect exceptions to maintain stable SSE connections in production.