快速结论:该问题出现在使用 lh agent run --json 运行 agent 时,如果 WebSocket 静默(无重置超时的真实流事件)超过 PROGRESS_TIMEOUT(300_000ms),CLI 会因 if (options.json) throw error; 直接抛出错误而不去查询 getOperationStatus,从而把“安静的活跃运行”误判为失败。优先排查 WebSocket 是否中途断开、进程是否仍在服务端运行,以及是否升级到了包含 #20389 的版本。
适用环境:Issue 基于 apps/cli/src/utils/agentStream.ts、apps/cli/src/commands/agent.ts、apps/cli/src/api/client.ts 的源码分析(commit 07c31121),未提供具体操作系统、Python、CUDA、显卡或依赖版本信息。
最快修复方案:升级到包含 #20389 的版本。该 PR 已按 Issue 中提出的方向修复:静默仅触发状态查询而不作为失败结论。--json 不再重新抛出,改为像 text 模式一样轮询,进度输出到 stderr,stdout 保持单个 JSON array,退出码反映结果;每个 getOperationStatus 请求通过 AbortSignal + 30s race 限定边界,超时或失败会重试,连续 3 次失败以 exit 3 结束。
注意事项:#20389 的行为是源码级修复并经过 CLI 实测(WS 以 1011 关闭后轮询 running → done 并 exit 0),但原始 Issue 本身并非端到端复现。--timeout <seconds> 为新增 opt-in 参数,超时后以 exit 3 结束并提示使用 lh agent status <op>,不会终止服务端运行;其余值(60s 静默窗口、10s 轮询、30s 请求超时、3 次失败)仍为固定值。
问题场景
用户通过 lh agent run 运行 agent 任务,并以 --json 模式输出。当 WebSocket 连接在运行过程中保持打开但长时间没有产生可重置进度截止时间的真实流消息时,运行本身在服务端可能仍然处于活跃状态,但 CLI 会直接抛错退出。Issue 特别指出,在 #19597 中 L4XB 描述的 gateway 场景下,即使心跳确认(heartbeat_ack)也不会重置该截止时间。
Issue 还指出固定轮询间隔与缺少显式请求截止时间的问题可能一并暴露:轮询循环在 agent.ts:909 处 await getOperationStatus.query,而 elapsed-time 检查位于 agent.ts:966,因此如果该查询一直 pending,轮询截止时间检查无法执行。
报错原文
lh agent run timeout policy: quiet active runs under --json and in-flight polling requests
相关源码行为(Issue 中引用):
if (options.json) throw error;
PROGRESS_TIMEOUT = 300_000
HANDSHAKE_TIMEOUT = 30_000
POLL_MS = 10_000
UNREADABLE_LIMIT = 3
DEADLINE_MS = 60 * 60_000
原因分析
最可能的原因是超时策略把“无进展”直接当成“运行失败”。在 --json 模式下,WebSocket 进度截止时间到期后触发 reject,而 agent.ts:489 中的 if (options.json) throw error; 跳过了轮询回退路径,因此 CLI 未检查 getOperationStatus 就退出了。Issue 明确说明这不能证明服务端运行已经失败。
其次(可能原因):固定轮询循环中的 getOperationStatus.query 调用没有在请求层绑定轮询截止时间,若请求一直挂起,循环中的 elapsed-time 检查就没有机会运行。Issue 指出客户端 httpLink 配置(client.ts:73-79)未将请求显式绑定到轮询截止时间,而新增轮询测试只覆盖已返回的查询结果,没有覆盖持续 pending 的查询。
Issue 也提醒:这些是代码层面的发现,而非针对部署环境的端到端复现;固定默认值是否应可配置属于策略问题,不能据此断定每个固定默认值都有缺陷。
环境排查
- 确认 CLI 是否包含 #20389 的修复;若不含,则存在本 Issue 描述的行为。
- 检查
--json模式运行时的 stdout/stderr 分离情况,以及进程退出码。 - 确认 WebSocket 是否在运行中关闭(例如 1011),并观察此后 CLI 是否会轮询
getOperationStatus。 - 核对 agent 任务在服务端是否仍报告为
running。 - Issue 未提供操作系统、Python、CUDA、PyTorch、显卡或依赖版本信息,这些不作为本问题的必要排查项。
解决步骤
- 若仍在使用旧版本,升级到包含 #20389 的版本,这是该 Issue 中被验证的直接修复。
- 在
--json模式下重新运行:修复后进度写入 stderr,stdout 保持单个 JSON array,退出码携带最终结果。 - 若 WebSocket 中途以 1011 关闭,确认 CLI 是否改为轮询
getOperationStatus直至得到done。 - 对静默但活跃的运行:确认无真实流消息 60s 后 CLI 是否触发状态查询,并且当服务端报告
running时是否继续等待,而不是判为失败。 - 如需控制等待时长,可使用新增的 opt-in 参数
--timeout <seconds>;超时后以 exit 3 结束并提示lh agent status <op>,不会终止服务端运行。 - 查询每个状态请求是否受 AbortSignal + 30s race 约束;连续 3 次超时或失败是否以 exit 3 结束而非当作成功。
验证方法
Issue 中列出的验证点:一个被服务端报告为 running 的安静运行,在 60s 和 120s 被探测,并在 150s 时仍在被等待,而不是被判失败。以 --json 模式运行且 WS 在运行中关闭(1011)时,旧行为是 canary 未检查状态即 exit 1,#20389 后则轮询 running → done 并 exit 0。退出码在 lh agent run --help 中有记录:0 done,1 failed/interrupted,2 waiting for human,3 not confirmable。
参考来源
相关 Issue:#19597、#19543;修复 PR:#20389。
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。


