[Bug] lh agent run never exits on the default WebSocket transport (–sse exits normally), CLI 0.0.54

这个报错通常出现在使用 LobeChat CLI( @lobehub/cli 0.0.54)执行 lh agent run 时,默认的 WebSocket gateway 传输在运行结束后不会让进程退出;如果换成 --sse 能正常退出,优先排查 WebSocket 路径是否在流认证后一直没收到终止

快速结论:这个报错通常出现在使用 LobeChat CLI(@lobehub/cli 0.0.54)执行 lh agent run 时,默认的 WebSocket gateway 传输在运行结束后不会让进程退出;如果换成 --sse 能正常退出,优先排查 WebSocket 路径是否在流认证后一直没收到终止事件(或认证失败后落入轮询兜底)导致进程被保留。

适用环境:Issue 中已确认的环境:LobeChat CLI @lobehub/cli 0.0.54;self-hosted LobeHub v2.2.17;通过 SSH 无 TTY 执行、stdout 重定向到文件;默认 WebSocket gateway 传输(agent gateway 8787、device gateway 8788)。Issue 未提供操作系统、Python、CUDA、显卡等信息。

最快修复方案:暂无确认的一步修复方案。Issue 中明确验证过的变通做法是在命令上加 --sse,让同一条命令正常返回退出码 0。

注意事项:--sse 只是绕过默认 WebSocket 传输的变通方案,不是对 WebSocket 路径本身的修复;Issue 中也没有配置项可以持久化传输选择。用 timeout 包裹调用也只是兜底。该问题在报告者环境中后来已不可复现,但讨论中确认至少存在两条不同的失败路径,因此不要假设单一原因。

问题场景

用户在自托管 LobeHub v2.2.17 上使用 CLI 0.0.54,执行 lh agent run -a <agent-id> -p "hello"。根据 --help,默认走 WebSocket gateway,--sse 才强制使用 SSE 流。问题发生在非交互式调用场景(例如通过 SSH 无 TTY 执行并把 stdout 重定向到文件):服务端运行其实已经完成,assistant 回复已经写入 topic,但 CLI 进程始终不退出,必须手动 kill,且不会产生任何退出码,无法放进脚本使用。

报错原文

[Bug] lh agent run never exits on the default WebSocket transport (--sse exits normally), CLI 0.0.54

Gateway auth failed: signature verification failed

[WARN] Live stream unavailable (Agent gateway WebSocket failed: [object ErrorEvent]). Polling run status every 10s…
[INFO] Run status: unknown

原因分析

根据讨论中对 CLI 源码 apps/cli/src/utils/agentStream.ts 的分析,streamAgentEventsViaWebSocket 只在 agent_runtime_end、session_complete、onerror 或 onclose 时 resolve。如果 WebSocket 完成认证后不再收到任何终止事件,这些条件都不会触发;同时 30 秒心跳是普通 setInterval,没有超时和上限,会一直把事件循环撑住。这正好解释了与 --sse 的不对称:streamAgentEvents 会读到响应体结束,服务端关闭 SSE 响应就能自然结束函数,而 WebSocket 路径没有等价的“放弃”分支。

讨论中还确认了另一条可能路径:先出现 Gateway auth failed: signature verification failed,然后进入“认证失败 → 轮询兜底”,轮询状态为 unknown,也会表现为进程不退出。报告者修正 agent gateway 的 JWKS_PUBLIC_KEY 使其与 app 的 JWKS_KEY 配对,并在 CLI access token 刷新/重新签发后,签名验证在 device gateway (8788) 和 agent gateway (8787) 上都通过,原先的挂起症状随之消失。因此这可能是认证/密钥对问题与 WebSocket 终止事件缺失两类原因叠加。

环境排查

  • 确认 CLI 版本是否为 @lobehub/cli 0.0.54,LobeHub 是否为自托管 v2.2.17(或同一代码路径的其他版本)。
  • 确认调用方式:是否默认使用 WebSocket gateway(未加 --sse),是否在 SSH 无 TTY 下运行、stdout 是否重定向。
  • 检查 agent gateway 的 JWKS_PUBLIC_KEY 是否为 app JWKS_KEY 的公钥半部分,两者是否成对。
  • 检查是否存在 Gateway auth failed: signature verification failed,以及 CLI access token 是否在密钥修正后重新签发/刷新过。
  • 分别确认 device gateway (8788) 和 agent gateway (8787) 上的签名验证是否都通过。
  • Issue 未提供操作系统、Python、CUDA、PyTorch、显卡信息,无需排查这些项目。

解决步骤

  1. 先用 --sse 复跑同一条命令,确认问题只在默认 WebSocket 传输下出现:lh agent run -a <agent-id> -p "hello" --sse > /tmp/out.json 2>&1。Issue 中该命令正常退出,退出码 0。
  2. 排查网关认证:确认 agent gateway 的 JWKS_PUBLIC_KEY 是 app JWKS_KEY 的公钥半部分。如果不是一对,先修正密钥。
  3. 修正密钥后,刷新或重新签发 CLI 的 access token,否则旧 token 仍由旧密钥签名,验证仍会失败。之后确认 device gateway (8788) 和 agent gateway (8787) 的签名验证都通过。
  4. 在不加 --sse 的情况下复跑,并加上 -v --json 观察缓冲区事件:lh agent run -a <id> -p "hello" -v --json > /tmp/out.json 2>&1 &,等服务端回复落地后查看 tail -40 /tmp/out.json。
  5. 根据 JSON 输出判断方向:如果没有输出任何 JSON,说明 agent_runtime_end / session_complete 从未到达 CLI,属于 WebSocket 路径缺少终止保证;如果 JSON 正常打印但进程仍挂起,说明终止事件已到达,是 resolve 之后有别的东西持有事件循环。
  6. 如果确认是终止事件从不送达,按讨论中的方向,修复思路不是“识别另一种事件形状”,而是让 WebSocket 路径获得 SSE 天然具备的完成保证:在 operation 到达终止状态时就 settle,而不是等待可能永远不发的流消息。

验证方法

在非交互式、无 TTY、stdout 重定向的条件下执行 lh agent run:理想结果是出现 Gateway authenticated → ✓ Agent finished → 进程自行退出;配合 --json 时输出以 {"type":"agent_runtime_end"} 结尾并正常退出。也可用 pgrep -af "lh agent run" 确认没有残留进程,并检查返回的退出码是否为 0。若仍挂起,回看 JSON 是否打印,以区分“终止事件未到达”和“resolve 后被其他句柄挂住”。

参考来源

lobehub/lobe-chat #19543

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 29092

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注