Skip to content

[bug] v0.8.0 升级后一发消息宿主即崩:v0 abort-cause 清扫未完成(EXC_BREAKPOINT on libuv-worker) #34

Description

@WuFenG-Hub

IDBots v0.8.0:升级后「一发消息宿主即崩」(EXC_BREAKPOINT / libuv-worker)

可直接作为 issue 正文提交。证据等级已逐条标注[已证实] / [强关联·未证实] / [未核实]

环境

IDBots 0.8.0(build 0.8.0),commit f83b886e = 官方 tag v0.8.0
上游 metaid-developers/IDBots
系统 macOS 26.6.2 (25G83),arm64
内嵌运行时 @deepseek-ai/dsh 0.1.5-rc.2Contents/Resources/dsh-runtime
发生时间 2026-09-12
升级路径 v0.7.0 → v0.8.0

症状

启动正常,界面正常。在既有会话里发一条消息 → 宿主主进程立刻崩溃退出(macOS 弹出「IDBots 意外退出」)。新建会话未观察到崩溃。

崩溃签名(4 次复现,完全一致)[已证实]

  • bug_type=309exception.type=EXC_BREAKPOINTsignal=SIGTRAPtermination.indicator="Trace/BPT trap: 5"
  • 故障线程名 libuv-worker(线程池工作线程);主线程当时正常停在 AppKit 事件循环
  • 故障帧 frame[0].imageOffset = 0x5500dc8(Electron Framework)—— 4 次崩溃完全相同,即确定性崩溃点
  • 故障指令 atPC 首条为 0xD4200000brk #0)→ 原生层主动陷阱,不是内存错误、不是 JS 异常
  • 崩溃进程中未加载任何第三方 .node 原生插件(koffi/sharp/node-pty 均属 dsh-runtime 子进程)
  • 诊断字段 asi / abort_cause / is_js_error 全部缺失ELECTRON_ENABLE_LOGGING=1 捕获的 stderr、系统统一日志、应用 main.log 三处均零输出

复现条件

  1. 机器上存在 0.8.0 之前版本的会话数据
  2. 其中至少一个会话在旧内核上被 steer 或 cancel 过(该轮 turn/end 的 abort cause 会被写成裸字符串
  3. 打开该会话并发一条消息

→ 命中率很窄,因此社区几乎无人碰到、也就无人上报。

证据链

  1. 崩溃报告:4 份 .ips 同签名、同一故障偏移 [已证实]

  2. logs/cowork.log 停在固定位置Resolved API configmemory:promptBlocksDSH reasoning effort 之后再无输出;应用 main.log 同样在 [GigSquare Rating] updated 0 services 后戛然而止 [已证实]

  3. 预热失败垫刀:每次启动 prewarmDshRuntime 都失败——

    [WARN] [prewarmDshRuntime] Warmup failed; first turn will cold-start
    error: Provider 'deepseek' does not offer enabled model 'deepseek-v4-flash-vision-exp';
           provider selection is required.
    

    于是走冷启动,把清扫推迟到首轮发言才执行,这正是"一发言就崩"的由来 [已证实]

  4. 清扫「做了一半」的量化铁证 [已证实]

    backups/v0-abort-cause/ 下每个会话产物备份的创建时刻呈两段式分布

    备份时刻 份数 归属
    14:56 36 宿主那趟(崩死前做到的)
    15:14 / 15:15 42 + 56 = 98 事后用系统 Node 补跑完成的剩余部分
    合计 134 36 + 98

    而补跑那趟的返回值是 {"scanned":806,"patched":98,...} —— 补跑的 98 与备份新增的 98 完全吻合
    即:宿主那趟只在 36 个会话上写出了备份就死了,剩下 98 个从未被处理。

  5. marker 缺失 [已证实]

    • marker dsh-sessions/v0/.v0-abort-cause-sanitized 在补跑前全盘搜索不存在ls 明确返回 No such file or directory)
    • sanitize-v0-abort-cause.mjs 自身逻辑:marker 不在 = 这一趟从未完成,宿主下次启动会全量重跑
  6. 时间吻合 [已证实]:最早的备份目录时间戳为 09-12 14:56,首次崩溃为 14:56:12 —— 同一分钟

根因判断

已证实部分:v0.8.0 引入的一次性清理 sanitize-v0-abort-cause.mjs(位于 dshKernel.ensureRuntime,在明文→zstd 编码迁移之后执行)从未跑完,marker 未落地,宿主每次冷启动都重跑,并在首轮发言时崩溃。

强关联、尚未证实部分:崩溃发生在该清扫内部

⚠️ 反证:把同一份 sanitize-v0-abort-cause.mjs系统 Node v24.12.0 单独执行,跑得干干净净、零崩溃:
{"scanned":806,"patched":98,"patchedEvents":253,"skipped":false}
因此"崩在清扫里"目前只有时间与调用链的关联,没有函数级证据——详见下方"未核实"。

已实测有效的绕过方式

App 未运行时,用系统 Node 单独执行该清扫使其落 marker:

// node --input-type=module
const m = await import('<IDBots.app>/Contents/Resources/dsh-runtime/lib/sanitize-v0-abort-cause.mjs')
await m.sanitizeV0AbortCauses('<userData>/dsh-sessions/v0', { log: console.log })

结果:marker 写入 {"version":1,"scanned":806,"patched":98,"patchedEvents":253,...};二次运行返回 {"skipped":true}
宿主重启后发消息不再崩溃,验收证据:DiagnosticReports 无新崩溃文件、main.log/cowork.log 继续正常推进。

给上游的修复建议

  1. 清扫必须可断点续跑。当前 marker 只在整趟扫完后一次性写入,任何中途失败(含进程死亡)都会导致每次启动重跑全量 806 个会话。建议改为 per-session 标记,或把已处理会话记入一个可增量提交的清单。
  2. 清扫不应在 ensureRuntime 的关键路径上阻塞。它属于一次性数据迁移,应异步化,且失败不应影响运行时启动。
  3. 补诊断打点:清扫入口/出口、每个会话处理前后、marker 写入前后各打一条日志。本次排查的最大障碍恰恰是"崩溃零输出",若有这些打点,定位时间可从数小时降到数分钟。
  4. prewarmDshRuntime 遇到已退休模型应自动折叠为当前模型(src/renderer/config.ts 里已有 'deepseek-v4-flash-vision-exp' → DEEPSEEK_DEFAULT_MODELS[0] 的别名映射),而不是 WARN 后放弃预热——预热失败把清扫挤到"首轮发言",直接改变了故障的表现形态。
  5. 崩溃可观测性:考虑启用 Crashpad 或给主进程装 uncaughtException/崩溃回调落盘,当前 4 份 .ips 全部无 asi、无 abort 原因,排障成本极高。

未核实 / 不确定性(不掩盖)

  • [未核实] 崩溃具体断在哪一行原生代码。Electron Framework 无符号表,.ips 中所有帧的符号解析均不可信(ares_dns_rr_get_ttl+巨大偏移),asi 字段缺失。
  • [未核实] 清扫在纯 Node 下可跑通、在 Electron 内会崩——若二者确有差异,方向应转向「宿主 Node 与系统 Node 在该 zstd/线程池路径上的行为差异」,而非清扫逻辑本身。
  • [未核实] 是否还有第二处引用旧模型 id。本次仅修掉配置目录中的死条目,路由逻辑本身未动。
  • 本次修复动作只做了两步:①用系统 Node 跑完清扫落 marker;②把 app_config.providers.commandcode.models[] 里已退休的 deepseek/deepseek-v4-flash-vision-exp 改名为 deepseek/deepseek-v4.1-flash未修改任何源码,未向仓库提交任何内容。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions