Skip to content

[BUG] DashMiddleware crashes on WebSocket scopes (FastAPI backend) #3636

Description

@benweinberg89

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 throughifscope["type"] notin ("http", "websocket"):
awaitself.app(scope, receive, send)
return# HTTP/WebSocket request handlingrequest=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:

ifscope["type"] !="http":
awaitself.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

importdashfromdashimporthtmlfromfastapiimportWebSocket, WebSocketDisconnectapp=dash.Dash(__name__, backend="fastapi")
app.layout=html.Div("Hello")
@app.server.websocket("/ws")asyncdefws_endpoint(websocket: WebSocket):
awaitwebsocket.accept()
awaitwebsocket.send_json({"msg": "hello"})
awaitwebsocket.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:

fromdash.backends._fastapiimportDashMiddleware_original_call=DashMiddleware.__call__asyncdef_patched_call(self, scope, receive, send):
ifscope["type"] =="websocket":
awaitself.app(scope, receive, send)
returnreturnawait_original_call(self, scope, receive, send)
DashMiddleware.__call__=_patched_call

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions