Kimi Code无声退出的根因,藏在"无日志"里:一个签名对上官方issue#3189
环境与版本:Windows;Kimi Code v2.1.1(2026-09-25 起在用,排查期间更新检查均返回无新版本);element-picker v1.1.3 → v1.1.4(自研浏览器插件,2026-09-29 上架 Edge 加载项);自动化测试基于 playwright-core + 本机缓存 Chromium。
背景
2026 年 10 月 6 日晚,在自研的经营驾驶舱系统中使用 element-picker 圈选页面元素、产出结构化 JSON 交给 AI 修改界面。当晚连续发生三起故障:element-picker 两个 bug,以及 Kimi Code 进程一次无声退出。以下为三起故障的完整复盘。
故障一:element-picker 面板遮挡待选元素
现象
控制面板固定在页面右上角,压住需要圈选的元素,无法选取。
根因与解决
面板定位 CSS 写死在右上角。修复方案:标题栏改为可拖拽(mousedown / mousemove / mouseup 三件套),加边界钳制防止拖出视口;拖动期间暂停悬停高亮以避免误触。
报障到修复用时 3 分钟,commit 1a1ca06,版本升至 v1.1.3。
故障二:Kimi Code 进程无声退出
现象
修复代码落盘、尚未提交时,Kimi Code 窗口消失:无报错、无弹窗,进程直接终止。
第一轮排查:方向错误
重启后在新会话中排查。AI 在 20:58 的 cls 会话日志中发现一行记录:
lastTurnReason: "cancelled"
据此判断"不是 bug,是人为取消"。但该 cls 窗口是退出之后重启的窗口——退出发生在前,重启在后。把"重启后的窗口"当成了"退出的窗口",故障对象锁定错误,结论方向整个反掉。这一轮误判消耗约 4 个工具调用。
教训:排查类问题,先锁定对象,再谈原因。
第二轮排查:无日志本身成为线索
针对退出发生时的原会话排查:wire.jsonl 在 20:58:15 戛然而止,第 4 步 Edit 工具结果刚落盘,turn 连 turn.ended 这一行都没来得及写。其余各层日志同样为零:kimi-code.log 零记录、Windows 事件日志零记录、Defender 零记录。自动更新排除:v2.1.1 自 9 月 25 日起在用,更新检查均返回无新版本。
随后将排查从"单次事件归因"切换为"模式取证":全量扫描所有历史会话的 state.json 与日志尾行,发现同一天 16:23 另一个会话出现相同特征——第 26 步 llm request 发出后永远没有响应记录,wire 戛然而止、无 turn.ended、四层日志零记录。
根因
两起猝死、同一签名。拿该签名检索官方仓库,命中 issue #3189(状态 OPEN):Windows 上进程无日志硬崩溃,根因为 libuv 异步句柄断言:
UV_HANDLE_CLOSING, src\win\async.c:76
fail-fast 机制直接中止进程,JS 层来不及写任何日志。有用户确认 v2.1.1 仍然复现,与本地版本一致。
结论:无日志本身就是日志签名。 进程消失 + 各层日志全零 = 原生层 fail-fast 的特征,该签名可直接用于匹配已知 issue,比盲猜终端宿主、杀毒软件、系统更新高效得多。
处置
| 时间 | 动作 |
|---|---|
| 21:15 | 按"根因未定位"记入故障台账(commit 09830a3) |
| 21:35 | 命中 issue #3189 后导出 debug 包备查,台账更新归因(commit c3ea51c) |
| 后续 | 台账记录跟踪规则:修复版本发布后升级并回归观察;在那之前随做随 commit |
故障三:iframe 内元素无法拾取
现象
弹窗内的元素无法选取。拾取器产出的 JSON 显示只能选中 el-dialog 外壳,内部元素全部摸不到。
根因
驾驶舱源码 matrix/index.vue 第 167 行,预览内容由 iframe 渲染:
<iframe sandbox="allow-same-origin">
iframe 内部的事件不冒泡到父文档,而拾取器的监听只挂在顶层 document 上——iframe 内部对拾取器是盲区。"监听挂在 document 就全覆盖"是错觉,iframe 是一堵事件边界墙。
解决
picker.js 重构为多文档挂载:
- 样式、提示条、事件监听挂进每一个同源 iframe 的文档;
- MutationObserver 兜住激活后动态插入的 iframe(即 el-dialog 打开的场景);
- iframe 内跳转自动重挂;
- 产出 JSON 新增 frameSelector 与 frameUrl 字段,标注元素所属 frame;
- 退出时逐文档清理,零残留。
验证
用 playwright-core 加本机缓存的 Chromium 搭自动化测试页,静态 iframe 与动态插入 iframe 两种形态均覆盖,6 项断言全绿:顶层拾取回归、iframe 内拾取、frameSelector 标注、样式采集、动态 iframe、退出零残留。commit db82a12,v1.1.4;dist 目录打出 element-picker-v1.1.4.zip(8 个文件,与上架包结构一致,commit c519383)。
预防:四条排查纪律
- 排查先锁对象,再谈归因。 报障时明确"是哪个窗口、哪个会话",避免整轮方向性误判。
- 无日志本身就是日志签名。 进程消失 + 各层日志全零是原生层 fail-fast 的特征;拼出"消失时间点 + 末行动作 + 各层零记录"的签名去检索官方 issue。
- iframe 是事件边界。 父文档监听摸不到 iframe 内部,同源 iframe 需逐文档注入;跨域 iframe 受同源策略限制,需在 iframe 侧注入脚本通过 postMessage 协作(本次未涉及,未实测)。
- 随做随 commit 是猝死保险。 本次 Kimi Code 20:58 崩溃时,故障一的修复已落盘待提交,resume 后零损失跑完。进程会猝死,落盘的代码不会。
FAQ
Q:"无日志即签名"如何迁移到其他崩溃排查?
三步:先确认进程是真退出还是假死;再把所有可能留痕的日志层逐层查一遍,全零本身就是信息;最后把"消失时间点、日志末行动作、各层零记录"拼成签名,检索官方仓库与 issue 列表。原生层崩溃 fail-fast 来不及写日志,该特征比错误码更稳定。
Q:同源 iframe 注入具体怎么挂?跨域 iframe 呢?
同源场景(如 sandbox="allow-same-origin"),父页面可直接访问 iframe 的 contentDocument,把样式与监听逐个文档挂入,再用 MutationObserver 盯住动态插入的 iframe。跨域 iframe 受同源策略限制,父页面无法访问 contentDocument,本次未遇到也未实测;思路是在 iframe 侧注入脚本,通过 postMessage 与父页面协作。
Q:issue #3189 官方修复了吗?
截至本文发布状态仍为 OPEN。当前应对:debug 包已导出备查,复发时打包附到 issue 下;台账记录跟踪规则,修复版本发布后升级并回归观察;在此之前随做随 commit,猝死只丢当轮。
Q:element-picker 能下载吗?
2026-09-29 已上架 Edge 加载项,搜索 element-picker 即可找到。本篇修复的 v1.1.4 更新包已打好,商店侧更新需走合作伙伴中心传包。
我是野生码农,AI实战派。17年全栈工程师,一人AI公司实践者。打造个人IP内容体系、软件全链路产品产线,拥有百套企业AI方案、200多个岗位Agent;自研知识库、AI数字员工系统,公开实战复盘。
本文所有内容均为本人 AI 实战的过程和结果,经 AI 整理后发布,无任何瞎编虚构内容。
这篇来自《数字员工养成记》系列。
关注我,看真的。
野生码农AI实战 · 全网同名
