Describe your context
dash 4.1.0rc0
fastapi 0.135.1
starlette 1.0.0rc1
uvicorn 0.41.0
- Python 3.13.0
- OS: macOS 15.5
Describe the bug
DashMiddleware.__call__ in dash/backends/_fastapi.py crashes when a WebSocket connection is made to the underlying FastAPI server. The middleware allows "websocket" scopes through its type filter (line 187) but then unconditionally creates a Starlette Request(scope, receive=receive) on line 192. Starlette's Request.__init__ asserts scope["type"] == "http", so any WebSocket scope raises an AssertionError.
This prevents using FastAPI's native @app.server.websocket() decorator alongside Dash.
The relevant code in DashMiddleware.__call__:
# Non-HTTP/WebSocket scopes pass through
if scope["type"] not in ("http", "websocket"):
await self.app(scope, receive, send)
return
# HTTP/WebSocket request handling
request = Request(scope, receive=receive) # crashes for websocket scopes
The comment says "pass through" for non-HTTP/WebSocket, but WebSocket scopes should also pass through since Request() only supports HTTP.
Expected behavior
WebSocket connections to custom FastAPI endpoints should work without crashing. The middleware should either pass WebSocket scopes through to the underlying ASGI app, or handle them with the appropriate Starlette WebSocket class instead of Request.
Suggested fix
Either change the guard to only process HTTP scopes:
if scope["type"] != "http":
await self.app(scope, receive, send)
return
Or, if the middleware needs to handle WebSocket scopes in the future, use WebSocket(scope, receive=receive, send=send) for websocket types.
Reproduction
import dash
from dash import html
from fastapi import WebSocket, WebSocketDisconnect
app = dash.Dash(__name__, backend="fastapi")
app.layout = html.Div("Hello")
@app.server.websocket("/ws")
async def ws_endpoint(websocket: WebSocket):
await websocket.accept()
await websocket.send_json({"msg": "hello"})
await websocket.close()
if __name__ == "__main__":
app.run(debug=True, port=8050)
Connect with any WebSocket client → AssertionError inside Starlette's Request.__init__.
Workaround
Monkey-patch the middleware to pass WebSocket scopes straight through:
from dash.backends._fastapi import DashMiddleware
_original_call = DashMiddleware.__call__
async def _patched_call(self, scope, receive, send):
if scope["type"] == "websocket":
await self.app(scope, receive, send)
return
return await _original_call(self, scope, receive, send)
DashMiddleware.__call__ = _patched_call
Describe your context
Describe the bug
DashMiddleware.__call__indash/backends/_fastapi.pycrashes when a WebSocket connection is made to the underlying FastAPI server. The middleware allows"websocket"scopes through its type filter (line 187) but then unconditionally creates a StarletteRequest(scope, receive=receive)on line 192. Starlette'sRequest.__init__assertsscope["type"] == "http", so any WebSocket scope raises anAssertionError.This prevents using FastAPI's native
@app.server.websocket()decorator alongside Dash.The relevant code in
DashMiddleware.__call__:The comment says "pass through" for non-HTTP/WebSocket, but WebSocket scopes should also pass through since
Request()only supports HTTP.Expected behavior
WebSocket connections to custom FastAPI endpoints should work without crashing. The middleware should either pass WebSocket scopes through to the underlying ASGI app, or handle them with the appropriate Starlette
WebSocketclass instead ofRequest.Suggested fix
Either change the guard to only process HTTP scopes:
Or, if the middleware needs to handle WebSocket scopes in the future, use
WebSocket(scope, receive=receive, send=send)for websocket types.Reproduction
Connect with any WebSocket client →
AssertionErrorinside Starlette'sRequest.__init__.Workaround
Monkey-patch the middleware to pass WebSocket scopes straight through: