Gemini CLI 报 Model stream ended 系列错误怎么办?三条流中断的判断方法
用 Gemini CLI 干活,时不时会撞上这几条:
✕ [API Error: Model stream ended with an invalid chunk or missing finish reason.]
✕ [API Error: Model stream ended with empty response text.]
[API Error: Premature close]
三条说的是同一类事:流式响应没有正常收尾。要么中途来了个不合法的数据块、要么缺了结束标记、要么干脆是空的、要么连接提前关了。
这篇必须先把话说在前面:这三条到核对日为止,都没有公认的解法。 本文不会给你一条「照做就好」的命令——那样写是不负责任的。能给的是:怎么判断这次是不是你的问题、怎么少受损失、以及在什么情况下值得往哪个方向查。
一、先说清楚证据状态
这三条各自对应 GitHub 上的 issue:
| 报错 | issue | 状态 | issue 里有解法吗 |
|---|---|---|---|
Model stream ended with an invalid chunk or missing finish reason. | #7851 | 已关闭,69 条评论 | 没有公认解法 |
Model stream ended with empty response text. | #10672 | 已关闭,50 条评论 | 没有公认解法 |
Premature close | #4230 | 已关闭,18 条评论 | 没有公认解法 |
三条都是「已关闭」状态。但已关闭不等于已解决——开源仓库里的 issue 可能因为陈旧、重复、或者相关代码变动而关闭。这三条的评论区主要是同样遭遇的汇报,而不是解法讨论。
Gemini CLI 仓库自带的 troubleshooting 文档里也没有收录这三条。 那份文档覆盖的是登录、安装、沙箱、CI 环境等类别,流式响应这块是空白。
所以本文的定位是:告诉你这是什么、怎么判断、怎么减损,而不是假装有答案。
二、这三条在说什么
流式响应的工作方式是:服务端一块一块地把内容发过来,客户端边收边显示,最后收到一个结束标记,表示这次响应完了。
三条报错分别对应这个过程的不同断点:
invalid chunk or missing finish reason:收到了不合法的数据块,或者流结束了但没有结束标记。客户端不知道这次到底算完了没有。empty response text:流正常结束了,但内容是空的。协议层面没错,语义层面等于什么都没说。Premature close:连接提前关了。这个最直接——传到一半断了。
这三条里,第二条最特别。它不是「传输出错」,是「传完了但没内容」——所以往网络方向查通常查不出什么。
三、能立刻做的判断
撞上之后,两分钟内可以做这几件事,把范围缩小:
第一,立刻重试一次。
流类问题很多是一次性的。重试就好,说明是偶发;每次都在同一个地方失败,那就有规律可循——看看是不是每次都在处理某个特定文件、或者输出到某个特定长度的时候断。
第二,看是不是集中在某一段时间。
如果是某半小时里密集出现、过后又正常,多半是服务端侧的短时状况。这种情况等就完了,改配置是白费力气。
第三,换个模型试。
不同模型的服务容量和行为不完全一样。换一个立刻通,说明跟你的环境无关。
第四,看有没有伴随 429 或 503。
Gemini CLI 里另外两条相关的报错是:
API Error: got status: 429 Too Many Requests.(issue #1502,已关闭,127 条评论)503 - The model is overloaded. Please try again later.(issue #7227,已关闭,70 条评论)
如果流中断和这两条交替出现,那更可能是服务端压力,而不是流协议本身的问题。 429 是你撞了额度或频率上限,503 是服务端过载——这两条都有明确的性质,比流中断好判断得多。
顺带说,429 值得先排除掉,因为它有明确的成因。按官方额度页,免费档的上限是:Google 登录 1000 次/天、60 次/分钟;未付费的 Gemini API Key 是 250 次/天、10 次/分钟。如果你走的是 API key 那条路,每分钟只有 10 次——跑个多步骤任务很容易撞上。
四、减损:控制它发生时你损失多少
既然没法保证它不发生,就把损失降下来。
任务切小。 这条对所有中断类问题都有效。一次让它跑十步,断在第八步,你得花时间确认前面七步做到什么程度;一次跑两三步,断了重来的成本很低。
断了先看,别急着重发。 中断之前它可能已经改过文件、执行过命令。直接重发同一个请求可能会重复执行。先看当前状态,再决定接着做还是重来。
脚本里用退出码判断。 官方文档给了一张退出码表:
| 退出码 | 类型 | 含义 |
|---|---|---|
| 41 | FatalAuthenticationError | 认证过程出错 |
| 42 | FatalInputError | 输入无效或缺失(仅非交互模式) |
| 44 | FatalSandboxError | 沙箱环境出错(Docker / Podman / Seatbelt) |
| 52 | FatalConfigError | settings.json 无效或有错 |
| 53 | FatalTurnLimitedError | 达到会话最大对话轮数(仅非交互模式) |
这张表里没有专门的流中断退出码——这本身就是有用的信息:如果你的脚本拿到的是 41、52 这种明确的码,那就不是流的问题,按表处理即可;拿到的是别的,才往流中断方向想。
用 --debug 看更多。 官方给的调试手段是 --debug 标志,交互模式下还可以按 F12 打开调试控制台。断流的时候开着它,能看到比界面上多得多的信息。
五、什么时候该怀疑自己这边
大部分情况下这几条不是你的问题,但有几种情况值得查一下自己:
每次都在同一个地方断。 这不像服务端的随机波动,更像是某个特定输入触发的。试试跳过那个文件、或者把那一步换个说法。
只在企业网/VPN 下复现。 中间设备可能在干预长连接。可以换个网络对比一次——这一步能直接分开「服务端问题」和「网络路径问题」。
只在某个特定终端或环境下复现。 换个终端试试。
伴随大量重试和超时。 那更可能是网络质量问题,而不是流协议问题。
六、还有一条长得像但不是一类的
顺带提一条容易被归到「它出错了」里的报错:
0 occurrences found for old_string
这条对应 issue #5629(已关闭)。它不是 API 错误,而是工具调用层面的——改文件的时候,要替换的那段旧内容没匹配上。成因通常是空白、缩进或换行的差异。
认出它的价值在于:这条完全不用往网络、服务端、流协议方向查。 它是个确定性的匹配问题,重试一百次也是同样的结果。
七、总结
- 这三条到核对日为止都没有公认解法,官方 troubleshooting 也没收录。本文不编解法。
empty response text最特别——流正常结束但内容为空,往网络方向查通常查不出东西。- 先重试一次,判断是偶发还是有规律。
- 看是否伴随 429 / 503——那两条性质明确,比流中断好判断。走免费 API key 的话,每分钟只有 10 次,很容易撞 429。
- 减损靠任务切小 + 断了先看再决定,别直接重发。
- 退出码表里没有流中断专用码——拿到 41/52 这种明确的码,说明不是流的问题。
0 occurrences found for old_string不是一类问题,那是文本匹配失败,重试无用。
本文引用的 issue 编号来自 google-gemini/gemini-cli 仓库,退出码表与调试方法来自该仓库自带的 troubleshooting 文档,额度数字来自官方额度与定价页,核对日 2026-08-08。issue 状态与产品行为会变化,以官方为准。