快速结论:这个报错出现在使用 LobeChat CLI 的 agent run 轮询回退(polling fallback)路径时:终端失败状态和被取消/中断的 run 被当成正常结束,返回退出码 0;服务端返回 null 结果时也会打印“finished (or expired)”并以 0 退出。优先排查你是否在用受影响的 CLI 版本,以及脚本是否依赖退出码判断成败。
适用环境:Issue 已确认的工具为 LobeChat CLI(lh agent run),源码路径 apps/cli/src/commands/agent.ts;问题在 commit 07c31121 的源码审查中被发现,并在 #20389 中修复。Issue 未提供操作系统、Python、CUDA、显卡或依赖版本信息,此处不做补充。
最快修复方案:升级到包含 #20389 的版本。修复后:终端失败/中断状态退出码为 1,waiting_for_human 退出码为 2,null 结果退出码为 3(不再暗示完成或过期),无法读取的响应在 3 次轮询后以退出码 3 放弃,重复请求失败也以退出码 3 结束、不再返回 0。
注意事项:这是面向调用方的行为变更(退出码语义改变),如果你的脚本此前把 0 一律视为成功、或在错误情况下继续执行后续步骤,升级后需要同步调整判断逻辑。Issue 中维护者建议这类 caller-visible 变更单独提 PR 并附 changelog,最终范围以维护者为准。
问题场景
用户通过 LobeChat CLI 执行 agent run,并依赖轮询回退路径获取 run 状态时触发该问题。具体表现为:命令在 run 实际失败、被中断或取消的情况下仍然正常返回,退出码为 0;当服务端没有返回操作状态结果(结果为 null)时,CLI 打印“Run is no longer tracked — finished (or expired).”并以 0 退出。调用该命令的脚本因此无法把这些异常结果与成功完成区分开来。
报错原文
Run error: model quota exceeded
Run is no longer tracked — finished (or expired).
说明:上面两行是 Issue 中引用的 canary 版本实际输出。修复后相同场景的输出变为:
✗ Agent failed: model quota exceeded
The server returned no status for this operation (unknown id, not visible to this account, or its run state already expired) — the run's outcome cannot be confirmed.
原因分析
根据 Issue 的源码审查和后续确认,原因是轮询回退分支没有区分终端状态:
- 在
apps/cli/src/commands/agent.ts:942-944,只要状态命中TERMINAL_RUN_STATUSES就直接return,而该集合同时包含成功状态和error、failed、interrupted以及取消/中止别名(853-866),没有为退出码做区分;只有存在 error 值时才会打印错误。此外interrupted当时不在终端集合内。 - 在
apps/cli/src/commands/agent.ts:915-917,当结果为空(!r)时直接打印“finished (or expired)”并return。缺失结果既不能证明 run 已完成或过期,也不能证明该操作此前被跟踪;这与“收到非空响应但状态无法读取”是不同情况。
结果就是依赖退出码的脚本在这些路径上无法区分失败、中断与成功完成。
环境排查
- 确认当前使用的 LobeChat CLI 版本是否包含 #20389 的修复。
- 确认触发路径是轮询回退(polling fallback),而不是 live-stream 路径(该 Issue 明确限定在轮询回退)。
- 如果是从源码运行,检查
apps/cli/src/commands/agent.ts中终端状态集合是否包含interrupted,以及!r分支的退出码处理。 - 检查调用脚本是否根据退出码判定成败;升级后需要按新契约(1/2/3)调整。
- Issue 未提供操作系统、Python、CUDA、显卡或依赖版本信息,无需按这些维度排查。
解决步骤
- 确认你遇到的确实是轮询回退路径的问题:agent run 报错或被中断时命令仍返回 0,或结果为空时打印“finished (or expired)”并返回 0。
- 升级到包含 #20389 的 LobeChat 版本(该修复已确认合入,是 Issue 中明确验证过的处理方式)。
- 升级后按新的退出码契约检查调用方逻辑:终端失败/中断为 1,
waiting_for_human为 2,null结果、无法读取的响应(3 次轮询后放弃)以及重复请求失败均为 3。 - 如需自行修改源码,可参照 #20389 的做法:把
interrupted补进终端状态集合,对失败/中断设置非零退出码,对null结果不再推断完成或过期,并给出明确的退出码。
验证方法
参考 Issue 中给出的真实 CLI 复现方式:让轮询返回 status: error,修复前 canary 打印 Run error: model quota exceeded 并以 0 退出,修复后应打印 ✗ Agent failed: model quota exceeded 并以 1 退出;让轮询返回 null,修复前以 0 退出并提示“finished (or expired)”,修复后应以 3 退出并输出无法确认结果的提示。用 echo $?(或 shell 等价方式)确认退出码符合预期,即视为修复生效。
参考来源
相关:#19597、#19543、#19613;修复 PR:#20389。
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。


