ChatGPTClaudeGrok / X / CursorGemini

DeepSeek Harness 桌面版更新报未能安全停止任务:停机超时原因与手动安装步骤

故障排查发布于 2026-10-30作者: DeepSeek Plugin 插件市场
DeepSeek HarnessDSH桌面端更新失败stop-failedshutdownmacOS
DeepSeek Harness 应用内更新弹「未能安全停止任务,更新尚未安装」,很多人以为任务还没跑完。其实这句提示只讲停机:包已下载校验通过,只是 host 没在 10 秒内退出,按字段拆开看再手动装。

「未能安全停止任务」这句话最坑的地方,是它长得像在说「你还有任务没跑完」,于是大家去关会话、停 Agent、等半天——然后发现完全退出 App、一个新会话都不开,点更新还是同样的错。 拆开看,这句话只讲了一件事:安装前要把 host 进程停掉,而 host 没在 10 秒内退出;更新包早就下载好并且校验通过了(#11187、#11361)。

DeepSeek Harness 第一层:它说的是「停机」,不是「任务」

这条提示对应 stop-failed 分支:更新前先锁任务状态拿快照,再调 backend.stop() 停 host;host 以不干净的退出收场,就抛这个错。

  1. 在 apps/desktop/src/main.ts 的更新流程里,先调 host.updateTasks('lock') 拿任务状态。
  2. 紧接着 backend.stop() 停 Host。
  3. 停机过程中 host 退出不干净,updateStopFailure 被设上,抛 stop-failed。
  4. 界面上显示的就是这句话(中文在 locale.ts:316,英文在 :137)。
  5. 同类还有三种失败各有各的文案:tasks-changed、tasks-unavailable、fullscreen-timeout。你看到的这一句只对应 stop-failed,别按另外三种去处理。
提示分支触发条件处理方向
stop-failedhost 10 秒内没退出彻底退出后手动安装
tasks-changed更新期间任务状态变了等任务结束再更新
tasks-unavailable拿不到任务快照重启后再试
fullscreen-timeout全屏退出超时退出全屏后重试

DSH插件 第二层:「exit 0 / signal null」说明它其实退干净了

技术详情里的 exit 0 和 signal null 意味着 host 是正常退出、没有被强杀;shutdown acknowledged true 意味着它收到了退出指令并确认。

  1. 点弹窗里的「查看技术详情」,把整行记下来。
  2. 对照字段含义:shutdown acknowledged: true 是收到了退出指令;exit: 0 / signal: null 是正常退出、没被 SIGTERM/SIGKILL 强杀。
  3. 所以「不干净」这三个字与进程最终状态无关,只与耗时有关。
  4. 这也说明包本身没问题:下载完成、sha512 与远端的 nightly-mac.yml 一致,权限也不需要 sudo。

DeepSeek Harness 第三层:「graceful deadline exceeded true」只是超了 10 秒

DesktopHost.stop 给 host 的宽限期写死 10 秒(1e4);超过就判为不干净,后面即使 SIGTERM、SIGKILL 成功退出,判定也不会改回来。

  1. 看判定条件:requireGraceful && (!graceful || exitCode !== 0 || !shutdownCompleted) 任意一项成立就抛错。
  2. graceful 来自 exitsWithin(exited, 1e4),也就是「10 秒内退出」。
  3. 超时之后依次是 SIGTERM(再等 5 秒)、SIGKILL(再等 5 秒);这三级都只是清理手段,不改变判定。
  4. 结果就是:host 明明 exit 0,仍被判「不干净」,更新被回滚到待装目录。
  5. 这条宽限期对慢机器和插件多的环境明显不够,社区建议放宽到 30 秒或改成可配置。

DeepSeek插件 根因:host 收到 shutdown 后从不调用 process.exit()

host 收到 shutdown 后执行 application.shutdown.shutdown(0)、回报 shutdown-complete、process.disconnect(),但没有 process.exit();清理完成不等于进程结束。

  1. 看 dsh-desktop-host/lib/index.js 的 stop():三步走完就结束,没有 exit。
  2. 看 profile-boot 的 createProcessShutdown():dispose() 完成后只调用 complete(code),也就是只设置 process.exitCode,然后指望事件循环自然排空。
  3. 只要有一个被引用的句柄——定时器、socket、watcher、FFI 回调通道——事件循环就永远不空。
  4. 社区做过隔离复现:只有 base + web-app 时 131 ms 退出;加一个 apply() 里只有 setInterval(() => {}, 1000) 的插件后,180 秒仍然活着。
  5. 所以这不是 koffi 的问题,koffi 只是 Windows 上必然存在的那个句柄。

同一个缺陷也影响普通退出:壳层先隐藏窗口再等 DesktopHost.stop(),于是 10 秒加 5 秒加 5 秒期间进程都还在,用户看到的就是「界面没了、进程还挂着」。

DeepSeek Harness 版本判定:修复在 master,没回移到 rc 线

master 上的 6252a29a(exit after acknowledged teardown,2026-10-18,配套测试 23fb3ea)在 shutdown 路径末尾加了 process.exit(),但它不是 rc.3 tag 的祖先。

  1. 查 tag:dsh-v0.2.1-rc.3 指向 1f81c047。
  2. 验证包含关系:git merge-base --is-ancestor 6252a29a 1f81c047 返回否,说明发布包里不可能带这个修复。
  3. 打包产物 dsh/node_modules/@deepseek-ai/dsh-desktop-host/lib/index.js 里,disconnect 之后确实没有 exit。
  4. 所以从 rc.2/rc.3 升级的用户都会撞上,不是个例。
  5. 社区在自己机器上按同样思路补了一行,实测 shutdown requested → shutdown-complete → exit 共 56 ms,Cmd+Q 后进程立即消失、无黑屏。

这里有个容易忽略的推论:宽限期只影响「是否判定成功」,不影响清理是否做完。也就是说,等 10 秒、发信号、强杀这一整套都会照常执行,只是最终结论已经被写成失败了。

DSH插件 手动安装:三条可执行路径

先让 host 真正消失,再直接跑已经下载好的待装包;安装器不会重新下载,会话数据也不受影响。

  1. 在 Windows 上确认进程真的没了:窗口的 × 只是隐藏到托盘,去任务管理器结束所有 DeepSeek Harness 进程。
  2. 找到待装包:%LOCALAPPDATA%\@deepseek-ai\dsh-desktop-updater\pending\ 下的安装 exe。
  3. 直接双击运行它,安装完成后会正常重启;~/.dsh/sessions 里的会话记录不会丢。
  4. macOS 上同理:osascript -e 'quit app "DeepSeek Harness"',等几秒,把 ~/Library/Caches/@deepseek-ai/dsh-desktop-updater/pending/ 里的 zip 用 ditto -x -k 解到 /Applications,再 xattr -dr com.apple.quarantine。
  5. 装完验证:codesign --verify --deep --strict 通过,版本号已变。
  6. 另一个减少遗留句柄的办法:更新前先把插件都停掉,让 host 有机会在 10 秒内退出。

注意事项

  1. 别反复点「重试」。每次都会重新跑一遍必然超时的停机判定。
  2. 别以为关窗口就退出了。托盘常驻是默认行为,进程还在,host 就还在。
  3. 别把三种同类失败混为一谈。只有 stop-failed 是停机判定,另外三种各有原因。
  4. 技术详情要看。DesktopHostUncleanExitError 的 message 只写进日志,弹窗会吞掉,那是唯一能定位下一步的线索。
  5. 手动装不会丢数据。会话、profile 都在 ~/.dsh 下,安装器只替换应用本体。
  6. 记住这条判定对普通退出同样生效。Cmd+Q 黑屏几十秒也是同一个原因,不是卡死。

如果你的技术详情里 exit 是非 0 或 signal 是 SIGKILL,那说明是真的没退干净,先查是不是有插件卡在清理里。

如果 exit 是 0、signal 是 null、只有 graceful deadline exceeded true,那这篇的结论就完全对得上,直接走手动安装。相关排查见 DeepSeek Harness 桌面端启动异常:没有窗口、启动崩溃、EMFILE 与 80 秒单核满载。

ChatGPTClaudeGrok / X / CursorGemini

常见问题

DeepSeek Harness 更新弹「未能安全停止任务,更新尚未安装」,是还有任务在跑吗?

不是,这句提示说的是 DeepSeek Harness 的 host 没停干净,不是还有任务在跑。它对应 stop-failed 分支:更新前先锁任务状态、再停 host,host 如果以不干净的退出收场就抛这个错。它说的是「host 没停干净」,与任务是否跑完无关。

技术详情里写着 exit 0、signal null,为什么 DeepSeek Harness 还说退出不干净?

因为 DeepSeek Harness 的判定条件里还有 graceful deadline exceeded true。DesktopHost.stop 只给 10 秒宽限期,host 收到 shutdown 后没有在 10 秒内退出,就会被判为不干净,即使它最终以退出码 0 正常结束。

为什么完全退出 App 再更新,DeepSeek Harness 仍提示停止失败?

因为问题不在你退出得干不干净,而在 DeepSeek Harness 的 host 收到退出指令后不会调用 process.exit()。只要有任意一个被引用的句柄(定时器、socket、watcher),事件循环就永远不排空,10 秒必然超。

这次的更新停止失败,在 DeepSeek Harness 哪个版本修好了?

DeepSeek Harness 的这个更新失败在 master 上的 6252a29a(exit after acknowledged teardown)修了,但按社区核对,rc.3 的 tag 指向 1f81c047,不是它的后代,所以 rc.2/rc.3 的发布包都不含这个修复。

不想等修复时,怎么把手上待装的更新包装进 DeepSeek Harness?

先在 DeepSeek Harness 里结束所有 DSH 进程(窗口的 × 只是隐藏到托盘),再直接运行 %LOCALAPPDATA%\@deepseek-ai\dsh-desktop-updater\pending 下那个待装 exe。安装器不会重新下载,会话记录在 ~/.dsh/sessions 不受影响。

相关术语

stop-failed
stop-failed 是 DeepSeek Harness 桌面更新器的一个失败分支,表示安装前的 host 停机判定失败。它区别于 tasks-changed、tasks-unavailable、fullscreen-timeout 三种同类失败,文案相同但含义不同。— https://github.com/deepseek-ai/deepseek-harness/discussions/11361
DesktopHostUncleanExitError
DesktopHostUncleanExitError 是桌面 host 退出判定失败时抛出的错误类型,技术详情里会带上退出码、信号、是否确认 shutdown、是否超时四个字段。它的 message 只在日志里,弹窗会吞掉。— https://github.com/deepseek-ai/deepseek-harness/discussions/11187
graceful deadline
graceful deadline 是更新器给 host 的优雅退出宽限期,当前实现是 10 秒。超时后会依次发 SIGTERM、SIGKILL,但最终判定仍以「是否在宽限期内退出」为准。— https://github.com/deepseek-ai/deepseek-harness/discussions/11187
shutdown-complete
shutdown-complete 是桌面 host 完成清理后回给外壳的 IPC 消息。它只表示清理动作做完,不代表进程已经退出;缺少后续 process.exit() 时事件循环仍会挂住。— https://github.com/deepseek-ai/deepseek-harness/discussions/11187

来源