mirror of
https://github.com/open-webui/open-webui.git
synced 2026-03-11 08:15:00 -05:00
issue: Unavailable direct connection chat with multiple workers due to WebSocket/API routing mismatch #5601
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @ShirasawaSama on GitHub (Jun 20, 2025).
Check Existing Issues
Installation Method
Git Clone
Open WebUI Version
v0.6.15 (latest dev)
Ollama Version (if applicable)
No response
Operating System
MacOS 15.5
Browser (if applicable)
Edge 133
Confirmation
README.md.Expected Behavior
Actual Behavior
When using "direct connection" mode with multiple backend workers, chat requests may timeout due to WebSocket and API request routing to different worker instances.
Steps to Reproduce
.env
Logs & Screenshots
Additional Information
The current architecture has the following flow:
/api/v1/completions/completionsProblem: When
workers > 1, the WebSocket connection and the/api/v1/completionsAPI request may be routed to different worker instances, causing the system to wait indefinitely for responses that will never arrive.@ShirasawaSama commented on GitHub (Jun 20, 2025):
We can add the following print statement to the
generate_direct_chat_completion function in thebackend/open_webui/utils/chat.py` file:and to the
backend/open_webui/socket/main.pyfile:and to the
src/routes/+layout.sveltefile:At this point, if the above problem occurs, we can observe that the pid of the two processes is completely different, and at the same time, the process of the http request does not receive websocket data at all!
If the request is normal and does not time out, the output is the following:
@Zyfax commented on GitHub (Jun 20, 2025):
When operating with multiple instances,
WEBUI_SECRET_KEYhas to be identical on all.With docker, it random generates a key on boot and will therefor be a mismatch.
@ShirasawaSama commented on GitHub (Jun 20, 2025):
I have added this environment variable and it still behaves the same:
@tjbck commented on GitHub (Jun 20, 2025):
Any reason why you have to utilise multiple workers instead of multi-replica setup?
@ShirasawaSama commented on GitHub (Jun 20, 2025):
yes, my .env file:
@ShirasawaSama commented on GitHub (Jun 20, 2025):
In fact, I've tried multiple k8s pod deployments as well as multiple worker deployments. However either way, as long as the number of instances is greater than 1, there is a probability that it will cause the chat to get stuck on a direct connection.
In addition, I read the official introduction of socket.io, and the official redis adapter seems to support only server-side push with multiple workers. As for receiving, client's data is not pushed to all workers.
https://socket.io/docs/v4/redis-adapter/
@chemi392 commented on GitHub (Jun 23, 2025):
I am not using direct connections, but do have multiple workers running on my instance and am experiencing the same behavior as of v0.6.15
@hpavanatti commented on GitHub (Jun 24, 2025):
Same problem here, multiple workers running, and experiencing the same in version v0.6.15(in v0.6.11 it happens to!)
@Simon-Stone commented on GitHub (Sep 15, 2025):
This is still a problem in v0.6.28.
@Zyfax commented on GitHub (Sep 15, 2025):
Enable direct connection under Admin Panel, Settings, Connections
@ShirasawaSama commented on GitHub (Sep 15, 2025):
Yes, when you enable this option and use multiple workers, this issue arises.
@Simon-Stone commented on GitHub (Sep 15, 2025):
It's not that the option to make a Direct Connection does not show up in the user settings. That works fine.
The issue is that requests made using a client-side Direct Connection do not succeed. The message just shows the placeholder indefinitely.
I believe that I narrowed the issue down a little bit further: This only seems to happen when streaming responses. When Stream Chat Response is set to off, the requests seem to go through just fine.
Would be nice if others with the same issue here could confirm that.
@ShirasawaSama commented on GitHub (Sep 15, 2025):
Because non streaming requests do not require OpenWebUI for WebSocket broadcasting, there are no issues.
The root cause of the problem now is actually very clear, as I mentioned earlier, socket.io does not broadcast the websocket data sent by the client to all servers. It may be necessary to refactor the entire directly connected logic.
@Simon-Stone commented on GitHub (Sep 18, 2025):
Still an issue in v0.6.30
@jasonpnnl commented on GitHub (Oct 28, 2025):
Root Cause Analysis
The issue is caused by dynamic event handler registration in
backend/open_webui/utils/chat.py:95that only registers handlers in the local worker process, not globally across all workers.Current Broken Flow
backend/open_webui/utils/chat.py:85-98:What Happens with Multiple Workers
POST /api/v1/completions→ Worker A (random routing)sio.on(channel, message_listener)locally in Worker A's memorysio.call()RPC to browser → routes through Redis to Worker B (where WebSocket lives){'status': True}✅ (RPC succeeds, returns to Worker A)user_id:session_id:request_iduser_id:session_id:request_idawait q.get()waits foreverWhy Other Socket.IO Events Work
All other socket.io handlers use static registration at module load time:
backend/open_webui/socket/main.py:These handlers exist in all workers, so events can be processed regardless of which worker receives them.
Proposed Fix: Use Global Handler + Redis Pub/Sub Routing
Replace per-request dynamic handlers with a global handler that routes messages via Redis.
Implementation Steps
1. Add Global Data Structures
File:
backend/open_webui/socket/main.pyAdd at module level, after
sioinitialization:2. Add Global Socket.IO Handler
File:
backend/open_webui/socket/main.pyAdd new global handler:
3. Add Redis Pub/Sub Listener
File:
backend/open_webui/socket/main.pyAdd new async function:
4. Start Redis Listener on Startup
File:
backend/open_webui/main.pyIn the startup event handler:
5. Modify Direct Chat Completion Function
File:
backend/open_webui/utils/chat.pyImport the global registry at the top:
Replace lines 85-108 (the streaming handler setup):
6. Update Frontend to Use Global Handler
Frontend changes needed:
Find where the frontend emits streaming chunks for direct connections and update:
Example location to check:
socket.emit()calls with dynamic channel namesTesting the Fix
1. Deploy with Multiple Workers
2. Test Direct Connection Streaming
3. Verify Cross-Worker Routing
Add temporary debug logging to confirm routing works:
Check logs to ensure:
4. Load Testing
Verify all 100 requests complete successfully.
References
Summary
The bug occurs because dynamic
sio.on()handlers are registered per-request in the worker that handles the HTTP request, but streaming responses come through the worker that holds the WebSocket connection. With multiple workers, these are often different processes, causing messages to be silently dropped.The fix uses a global static handler (
@sio.on("direct-chat-stream")) combined with Redis pub/sub to route messages to the correct worker's queue, ensuring all workers can handle streaming responses regardless of which worker holds the WebSocket connection.@jasonpnnl commented on GitHub (Oct 28, 2025):
This is an AI suggestion for what is causing the problem that seems plausible from what I understand. I can confirm that having multiple uvicorn workers causes the described issue, but have not verified the above root cause or suggested solution. I posted it in case it helps a dev find and fix the issue.