快速结论:该报错发生在 Open WebUI 通过浏览器对直连(非 orchestrator)Open Terminal 进行 WebSocket 交互式终端连接时。原因是 Open WebUI 的 WS 代理在转发上游请求时未携带 X-User-Id 头,导致 open-terminal 的身份校验返回 4004 “Session not found”。优先排查 Open WebUI 与 Open Terminal 之间的 WS 代理是否正确转发用户身份头。
适用环境:Open WebUI v0.11.3,Open Terminal v0.12.4(ghcr.io/open-webui/open-terminal),后端代理模式且设置 OPEN_TERMINAL_MULTI_USER=true。Kubernetes + Cloudflare tunnel + Envoy Gateway 环境下复现,LAN 直连路径同样可复现,可排除网络层因素。
最快修复方案:暂无经过完整验证的一步修复方案。Issue 报告在确认根因为 Open WebUI 上游 WS 代理缺口后主动关闭,未给出经过验证的补丁。可优先尝试在 Open WebUI 的 routers/terminals.py 的 ws_terminal 函数中,为上游 WS 连接补充与 REST 代理一致的 X-User-Id 头(需自行修改代码或等待官方修复)。
注意事项:上述代码修改属于建议方案,Issue 中未验证实际效果;修改涉及 Open WebUI 源码,升级版本后可能被覆盖。另外,open-terminal 侧存在次要缺陷:4004 关闭路径不会清理 PTY 会话,连续失败可能导致会话堆积直至达到 16 个上限后返回 429。
问题场景
用户在 Open WebUI 中使用浏览器内置终端,连接到一个直连(非 orchestrator)的 Open Terminal 服务器。所有 WebSocket 连接尝试均被上游以 4004 “Session not found” 关闭;Agent/REST 路径正常工作,只有浏览器的交互式 WS 连接失败,因为只有该路径经过 Open WebUI routers/terminals.py 中的 WS 代理。
每次失败的连接都会在 Open Terminal 服务器上遗留一个存活的 shell + PTY 会话,直到达到会话上限(16 个),此后新会话会收到 429 错误。
报错原文
Browser terminal attach to direct (non-orchestrator) Open Terminal fails 4004: OWUI WS proxy omits X-User-Id header
4004 "Session not found"
原因分析
Open WebUI 与 open-terminal 之间的两条代码路径对用户身份的传递方式不一致:
- REST 代理路径(正常):Open WebUI 的
proxy_terminal在上游请求中写入X-User-Id头。open-terminal 的POST /api/terminals将其存储为session["user_id"],Agent 路径正常工作。 - WS 代理路径(异常):Open WebUI 的
ws_terminal将user_id放在上游 URL 的查询参数中(?user_id=<uuid>),且upstream_headers = {}(除可选的X-Terminal-Context-Id外),从不转发X-User-Id或X-Session-Id。
open-terminal 的 WS 所有权检查(open_terminal/main.py 的 ws_terminal)仅比较请求头中的用户身份:
if (
session["user_id"] != ws.headers.get("x-user-id", "")
or session["chat_id"] != ws.headers.get("x-session-id", payload.get("chat_id", ""))
):
await ws.close(code=4004, reason="Session not found")
因此 session["user_id"](uuid)与空的 header 不匹配,导致每次浏览器经过 OWUI 代理的连接都返回 4004。直接设置 header 的连接则可以正常工作。
环境排查
- 确认 Open WebUI 版本为 v0.11.3,Open Terminal 镜像版本为 v0.12.4(
ghcr.io/open-webui/open-terminal)。 - 确认 Open Terminal 为后端代理模式部署,且已设置
OPEN_TERMINAL_MULTI_USER=true。 - 在 Open WebUI Pod 内部直接复现(排除网络代理干扰),确认 LAN 路径同样复现,排除网关/隧道层问题。
- 可通过真实流量抓包确认:HTTP→WS 升级在 Cloudflare tunnel 和 LAN 路径均返回 101,非网关问题。
解决步骤
- 定位问题(可优先尝试):确认为 Open WebUI 的 WS 代理代码缺口,非网络或配置问题。可对比相同连接带与不带
X-User-Id头的表现:带 header 可正常连接并流式输出 PTY,不带则返回 4004。 - 修改 Open WebUI 源码(建议方案,未经验证):编辑
routers/terminals.py中的ws_terminal函数,为上游 WS 连接补充与 REST 代理一致的身份头:
upstream_headers = {
"X-User-Id": user.id,
}
# 当浏览器请求携带 X-Session-Id 时同时转发
- 注意安全性:
user已在_resolve_authenticated_connection中通过验证的 JWT 服务端解析,非信任客户端提供的值,与 REST 代理行为一致。 - 防御性应对(可选,open-terminal 侧):在 open-terminal 侧,当 header 缺失时回退到
?user_id=查询参数,可增强直接 WS 客户端的鲁棒性。该改动可单独提交到 open-terminal 仓库。
验证方法
修改后重新启动 Open WebUI,通过浏览器发起交互式终端连接,确认能够正常建立 WebSocket 会话并看到 PTY 输出流。同时确认 open-terminal 日志中不再出现 4004 关闭记录,且无遗留的 shell 进程堆积。
参考来源
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。

![[RFC] RL CI Matrix for vLLM: Behavioral + Physical + Protocol Coverage](https://www.chat-gpts.plus/wp-content/uploads/2026/09/45585-41fefa5f-768x403.jpg)
![[RFC]: Partial Cache Hits for Hybrid Models](https://www.chat-gpts.plus/wp-content/uploads/2026/09/45702-8c7caa82-768x403.jpg)