Gemini CLI 报 Cannot read properties of undefined (reading 'candidates') 怎么办
用着用着,蹦出一条一看就不像业务报错的东西:
API Error: Cannot read properties of undefined (reading 'candidates')
或者带方框的那种形式:
✕ [API Error: Cannot read properties of undefined (reading 'candidates')]
对应的是 GitHub 上的 issue #18621 和 #18622(都已关闭)。
这篇先讲清楚这条报错的性质——因为它的性质决定了哪些排查方向是白费力气。绝大多数人在这条上浪费时间,是因为把它当成了配置问题。
一、这是一条 JavaScript 运行时错误,不是 API 业务错误
Cannot read properties of undefined (reading 'xxx') 是 JavaScript 里最常见的一类错误,意思是:代码想从一个东西里取 xxx 这个属性,但那个东西是 undefined。
具体到这里:candidates 是 Gemini API 响应体里承载模型输出的那个字段。客户端拿到响应之后,去读 响应.candidates,结果发现要读的那层是空的。
所以真实发生的事情是:服务端返回了一个客户端没预料到的形状,客户端在解析时崩了。
这有几个直接推论,能帮你省掉大半排查:
- 不是你的配置写错了。 配置错会报配置类错误(Gemini CLI 有专门的
FatalConfigError,退出码 52) - 不是认证问题。 认证问题有自己的报错和退出码(41)
- 不是模型”回答得不好”。 这个阶段还没到内容层面,是解析就挂了
- 不是你的 prompt 写得有问题。 同样的 prompt 重试可能就过了
换句话说:报错文本里出现 Cannot read properties of undefined 这种措辞时,问题几乎一定在程序内部,而不是在你的输入上。
二、证据状态:没有官方定论
必须说清楚:
- issue #18621(已关闭,43 条评论)和 #18622(已关闭)都没有给出公认的根因和解法
- Gemini CLI 仓库自带的 troubleshooting 文档没有收录这条——那份文档覆盖的是认证、安装、沙箱、CI 环境、退出码,没有这一类
所以本文不给「解法」。下面给的是判断方法和减损做法。
三、能做的排查
第一,重试。
这条报错的性质决定了它很可能是偶发的——服务端某次返回了异常形状。重试一次,多半就过了。
如果重试稳定复现,那才值得往下查,因为这说明有某个确定的触发条件。
第二,看是不是跟某个特定输入绑定。
稳定复现的话,试着改变输入:
- 换一个文件
- 把那个请求拆小
- 换一种说法
如果换了输入就好了,那就锁定了触发条件——虽然你修不了客户端的解析逻辑,但你可以绕开那个输入。
第三,升级版本。
这类解析崩溃属于典型的「客户端没处理好边界情况」,通常会在后续版本里加上判空处理。所以:
npm install -g @google/gemini-cli@latest
(如果你是从源码跑的,就 pull 最新代码再 npm run build。)
这条是本文性价比最高的一条建议。 issue 已经关闭意味着相关代码大概率动过——用旧版本撞一个可能已经被修掉的问题,最不划算。
第四,用 --debug 看细节。
官方给的调试手段是 --debug 标志,交互模式下可以按 F12 打开调试控制台。这类崩溃开着 debug 能看到更完整的堆栈和响应内容,判断是哪一层出的问题。
四、什么方向是白费力气
省时间的关键往往是知道别查什么:
- ❌ 别去改
settings.json——配置错会报FatalConfigError(退出码 52),不是这条 - ❌ 别去重新登录——认证问题会报认证类错误(退出码 41)
- ❌ 别去查网络和代理——那类问题的表现是连不上、超时、证书错误,不是解析崩溃
- ❌ 别去调 prompt 措辞——这个阶段还没到内容层面
- ❌ 别去清缓存重装——除非你要顺便升级版本,否则重装同一个版本不改变任何东西
上面这五条是搜这类报错时最容易搜到的「万能建议」,但它们对这条报错都不对症。
五、跟它容易混的几条
Gemini CLI 里几条报错都以 API Error: 开头,但性质完全不同:
| 报错 | 性质 | 该往哪查 |
|---|---|---|
Cannot read properties of undefined (reading 'candidates') | 客户端解析崩溃 | 重试、升级版本;别查配置和网络 |
got status: 429 Too Many Requests | 额度或频率上限 | 查你在哪一档(免费 API key 只有 250/天、10/分钟) |
503 - The model is overloaded | 服务端过载 | 等、换模型 |
Model stream ended with empty response text | 流结束但内容为空 | 重试;无公认解法 |
Premature close | 连接提前关闭 | 重试;无公认解法 |
0 occurrences found for old_string | 文本匹配失败(工具层,不是 API) | 检查空白/缩进/换行差异;重试无用 |
这张表里,只有 429 有明确且可自查的成因。按官方额度页,Google 登录是 1000 次/天、60 次/分钟;未付费的 API Key 是 250 次/天、10 次/分钟。撞 429 时先确认自己在哪一档。
最后一条 0 occurrences found 值得记住,因为它跟本文这条形成对照:一个是随机的(重试可能好),一个是确定的(重试一定不好)。分清「重试有意义」和「重试没意义」,能省掉大量无效操作。
六、减损
跟其他不可控的偶发故障一样,重点是控制损失:
任务切小。 断在第三步比断在第三十步好收拾。
断了先看当前状态。 它崩之前可能已经改过文件。直接重发同一个请求可能重复执行。
脚本里靠退出码分流。 官方退出码表里 41 是认证、42 是输入无效(仅非交互模式)、44 是沙箱、52 是配置、53 是达到最大轮数(仅非交互模式)。这条解析崩溃不在表里——所以脚本拿到的如果是表里的码,就按表处理;不是表里的码,才考虑是这类内部错误,走重试逻辑。
版本别落太远。 这是本文重复第二遍的建议,因为对这类问题它的成功率最高。
七、要报给上游的话,怎么报才有用
这类偶发的解析崩溃,如果你能稳定复现,报上去是有价值的——因为绝大多数报告者都做不到稳定复现,能复现的报告含金量很高。
报之前先把这几样准备好:
- 完整的报错堆栈,用
--debug跑一次拿到(交互模式下按 F12 打开调试控制台) - 版本号:
gemini --version,以及node -v - 稳定复现的最小步骤——把输入删到不能再删还能复现的程度
- 授权方式:Google 登录、API key、还是 Vertex AI。这一条经常被漏掉,但它决定了走的是哪条服务路径
有一样不要贴:把日志、调试输出往公开仓库贴之前,先确认里面没有你的代码内容和凭证。调试输出通常包含请求体,而请求体里可能有你项目里的源码片段。这一条跟具体是哪个工具无关,是通用的习惯。
如果做不到稳定复现,报告的价值就有限了——这种情况下更实际的做法是先升级到最新版本,看问题是不是已经被修掉。
八、如果它频繁到影响干活
偶发一两次可以重试过去,如果频率高到干扰工作,有几件事可以做:
- 降低单次任务的复杂度。请求越复杂、涉及的工具调用越多,中间出现异常响应的机会越多
- 换授权方式试一次。Google 登录和 API key 走的路径不同,换一条能判断问题是不是绑定在某条路径上
- 换模型试一次。不同模型的服务端行为不完全一样
- 检查版本。这是本文第三次提到升级,因为对这类客户端解析问题,它的成功率确实最高
如果换授权、换模型、升版本都试过还是高频复现,那基本可以判定跟你的环境相关,值得按上一节的做法去报告。
九、总结
- 这是客户端解析响应时的 JavaScript 运行时错误,不是配置、认证、网络或 prompt 的问题。
- issue #18621 / #18622 都已关闭但没有公认解法,官方 troubleshooting 也没收录。
- 先重试——这类偶发问题重试的成功率不低。
- 然后升级版本,这是性价比最高的一条。
- 别去改配置、重新登录、查网络、调 prompt——那几条是搜索结果里最常见的建议,但对这条都不对症。
- 分清哪些报错重试有意义(这条、流中断类)、哪些重试没意义(
0 occurrences found for old_string)。
本文引用的 issue 编号来自 google-gemini/gemini-cli 仓库,退出码表与调试方法来自该仓库自带的 troubleshooting 文档,额度数字来自官方额度与定价页,核对日 2026-08-08。