DeepSeek Harness 打开开发者工具就崩:inspector source closed

故障排查发布于 2026-10-20作者: DeepSeek Plugin 插件市场
DeepSeek HarnessDSHinspector开发者工具host 崩溃Cordispublisher
DeepSeek Harness 打开实验性开发者工具后 host 以 inspector Host source is closed 退出;本文拆解 publisher 的关闭竞态,并给三种避免与修复方式。

打开实验性「开发者工具」本来是为了看插件树,结果 host 直接退出,日志只有一行 dsh: fatal uncaught exception: Error: inspector: Host source is closed。 这个崩溃不是开发者工具本身坏了,而是 inspector 源关闭与快照发布之间的竞态:关闭动作先发生,排队中的 publish 仍会执行,setState 在 closed 状态抛错,宿主的 fail-loud 策略把它变成 exit(1)。

DeepSeek Harness 崩溃前的三个特征

一句话结论:崩溃发生在「关闭开发者工具之后」或「插件树频繁变化时」,而不是打开的一瞬间,且日志里只有 inspector source closed,没有插件堆栈。 对照检查:

  1. 打开开发者工具并观察几秒;预期不会崩,source 处于 open 状态。
  2. 关闭工具后等几秒;预期进程在这个窗口内退出,日志出现 Host source is closed。
  3. 搜索崩溃日志;预期只有 inspector/publisher,没有具体插件名,说明问题不在某个插件。

据讨论里的源码核对,publisher.ts:31 的 setState 在 closed 时 throw,而 publish()(:24)只静默返回;Cordis 树监听 src/shared/cordis/publisher.ts:22-23 不判 closed,观察者仅在 disposer 中置位(observer.ts:27/31/40-44),throw 落在 queueMicrotask(publish) 内没有 catch(#10069)。桌面端调试的其他入口可以对照 桌面调试配置。

DSH inspector 竞态是怎么排出来的

一句话结论:source 先关闭,微任务里的 publish 后执行;publish 只看「有没有观察者」,不看 source 是否还活着,setState 于是踩空。

触发链:

  • 用户关闭开发者工具,source 进入 closed。
  • 此时已有一个 publish 被排进微任务队列。
  • 微任务执行,遍历观察者并调用 setState。
  • setState 检查到 source closed,抛 Error。
  • 没有 catch,宿主 fail-loud,进程退出。

触发频率取决于插件树变化有多频繁:变化越多,排队的 publish 越多,关闭时越容易撞上。

DeepSeek Harness 开发者工具崩溃的三种处理

一句话结论:只想不崩就关掉实验开关,需要继续调试就控制关闭时机,要提交上游就改生命周期。

规避一:先关工具,再停插件

不改代码时,用操作顺序避开竞态——不要在插件树还在变化时关闭开发者工具。

  1. 关闭开发者工具前,先停止正在安装、启用或禁用插件的操作;预期不再产生新的树变化。
  2. 等插件列表状态稳定(没有 pending、没有 installing);预期队列有时间清空。
  3. 再关闭开发者工具;预期 source 关闭时没有 publish 在途。
  4. 关闭后不要立刻退出 DSH,给它几秒;预期剩余微任务先执行完。
  5. 观察日志,确认没有新的 publish 排队;预期不再出现 inspector 报错。

规避二:禁用实验性开发者工具

最省事的止损是关掉这个实验开关,等功能稳定后再启用。 适合你只是好奇点开、并不需要长期调试的情况。

  1. 打开设置 → 实验性功能,找到开发者工具开关;预期能看到当前开关状态。
  2. 关闭它并重启 DSH;预期启动阶段不再出现 inspector 错误。
  3. 正常使用一段时间;预期不再出现 inspector source closed。
  4. 如果确实需要调试,改用日志与 dsh 命令行诊断;预期仍能观察插件状态。

修法三:改生命周期,让 source 关闭时释放观察者

根治方向是「谁持有观察者,谁负责释放」,而不是让 setState 静默吞错。 适合维护 fork 或提交上游。

  1. 在 inspector source 的 close() 里遍历并清空观察者列表,同时把 pending 的 publish 标记为取消;预期没有观察者再接收状态变更。
  2. 给 publish() 加 source 状态判断:closed 时直接返回,不进入 setState;预期迟到的微任务变成空操作。
  3. 在 setState 的 throw 处补上下文字段(source id、关闭原因);预期日志能指出是哪个通道失败。
  4. 给 queueMicrotask(publish) 包一层 catch,至少把异常写进日志而不是直接退出;预期残余故障被记录而非致命。
  5. 补测试:关闭 source 后立即触发一次树变化;预期 host 不退出。

四种改动的优先级:释放观察者治本,优先做;publish 判 closed 成本低,能挡住大部分竞态;setState 补上下文提升可诊断性;微任务 catch 是最后一道保险,不建议单独使用。

DSH 插件调试后的验证与避坑

一句话结论:多次开关、每次都在插件树变化后关闭、再等十秒,才能证明修复覆盖了竞态窗口。 验证步骤:

  1. 打开开发者工具,连续触发三次插件启用/禁用;预期不退出。
  2. 关闭工具,等待 10 秒;预期 host 仍在运行。
  3. 再打开工具;预期 inspector 能重新连接。
  4. 重复五次,观察是否有延迟崩溃或日志里的 uncaught exception;预期没有。
  5. 用一个频繁更新状态的插件做压力测试;预期关闭后不再 publish 到已关闭的 source。

避坑要点:

  • 不要用「捕获所有未捕获异常」来掩盖问题,那会让其他真实崩溃也失去 fail-loud 保护。
  • 开发者工具是实验性功能,行为可能随版本变化;修复前先确认当前版本是否仍复现。
  • 崩溃可能发生在关闭后几秒,不要误以为是其他 DSH插件 导致的随机退出。
  • 讨论里没有给出官方修复版本,0.2.1-rc.2 仍可复现;升级后以 release notes 为准。

DeepSeek Harness inspector 常见问题

Q:DeepSeek Harness 开开发者工具就退出,日志只有 source is closed,怎么避免? A:DeepSeek Harness 退出是 inspector 源在发布快照时已被关闭的竞态。publisher 的 setState 在 source closed 时直接 throw,而调用它的 queueMicrotask 没有 catch,异常被 fail-loud 策略转成 exit(1)。临时避免方式是不要在插件树变化时关闭工具,或关闭后重启 host。

Q:DeepSeek Harness 的 inspector source closed 是插件问题还是核心问题? A:DeepSeek Harness 的 inspector source closed 是核心侧的生命周期问题。Cordis 树的监听者没有在 source 关闭时解除订阅,观察者只在 disposer 里置位;source 关闭后仍可能有 publish 排进微任务,setState 于是抛错。插件只是触发者。

Q:为什么 DeepSeek Harness 不在 publish 里 catch 异常,而让 host 退出? A:DeepSeek Harness 的 publish 在 closed 时只静默返回,真正的 throw 发生在 setState,而调用链在 queueMicrotask 里没有 catch;宿主采用 fail-loud 策略,未捕获异常直接退出。直接吞掉异常能止血,但会掩盖生命周期错误。

Q:修好后怎么验证 DeepSeek Harness 的开发者工具可以反复开关? A:DeepSeek Harness 的修复要连续开关开发者工具五次,每次开关后触发一次插件树变化,观察 host 是否稳定。同时看日志里有没有 inspector source closed 或 uncaught exception;再用一个频繁更新 inspector 的插件跑一轮,确认不会延迟崩溃。

常见问题

DeepSeek Harness 开开发者工具就退出,日志只有 source is closed,怎么避免?

DeepSeek Harness 退出是 inspector 源在发布快照时已被关闭的竞态。publisher 的 setState 在 source closed 时直接 throw,而调用它的 queueMicrotask 没有 catch,异常被 fail-loud 策略转成 exit(1)。临时避免方式是不要在插件树变化时关闭工具,或关闭后重启 host。

DeepSeek Harness 的 inspector source closed 是插件问题还是核心问题?

DeepSeek Harness 的 inspector source closed 是核心侧的生命周期问题。Cordis 树的监听者没有在 source 关闭时解除订阅,观察者只在 disposer 里置位;source 关闭后仍可能有 publish 排进微任务,setState 于是抛错。插件只是触发者。

为什么 DeepSeek Harness 不在 publish 里 catch 异常,而让 host 退出?

DeepSeek Harness 的 publish 在 closed 时只静默返回,真正的 throw 发生在 setState,而调用链在 queueMicrotask 里没有 catch;宿主采用 fail-loud 策略,未捕获异常直接退出。直接吞掉异常能止血,但会掩盖生命周期错误。

修好后怎么验证 DeepSeek Harness 的开发者工具可以反复开关?

DeepSeek Harness 的修复要连续开关开发者工具五次,每次开关后触发一次插件树变化,观察 host 是否稳定。同时看日志里有没有 inspector source closed 或 uncaught exception;再用一个频繁更新 inspector 的插件跑一轮,确认不会延迟崩溃。

相关术语

inspector
inspector 是 DeepSeek Harness 暴露插件树、服务和运行时状态的调试通道,开发者工具通过它读取快照。它有一个明确的 open/closed 生命周期。— https://github.com/deepseek-ai/deepseek-harness/discussions/10069
publisher
publisher 是 inspector 侧负责把状态变更推给观察者的组件,内部维护 source 状态与订阅列表。source 关闭后继续 setState 会抛错。— https://github.com/deepseek-ai/deepseek-harness/discussions/10069
fail-loud
fail-loud 是 DeepSeek Harness 宿主的错误策略:未捕获异常不静默吞掉,而是记录后退出进程,避免带病运行。它会把 inspector 这类局部错误放大成整机退出。— https://github.com/deepseek-ai/deepseek-harness/discussions/10069

来源