DeepSeek Harness 工具调用后崩溃 "reading 'prepare'"?核心包版本漂移排查

故障排查发布于 2026-08-27作者: DSH Plugin 插件中心
DeepSeek HarnessDSH pluginreading prepare版本漂移工具调用崩溃
DeepSeek Harness 工具调用后崩溃报 reading 'prepare'?根因是第三方插件把 @deepseek-ai/dsh-tools 声明为直接依赖,profile 解析到旧版核心包,Symbol 键跨份不相等。对比核心包版本、二分禁用插件即可恢复。

DeepSeek Harness 会话中途工具调用后崩溃、报 Cannot read properties of undefined (reading 'prepare'),且纯文本回复一切正常——根因通常是版本漂移:第三方插件把 @deepseek-ai/dsh-tools 声明为直接依赖,装进 profile 时解析到旧版本(实测 0.1.0-rc.8 vs 全局 0.1.1-rc.2),工具调度器的 Symbol() 键跨份不相等,agent loop 取到 undefined 直接崩。 对比 profile 与全局的核心包版本、二分禁用插件即可定位并恢复;这与「重复核心包」同源但触发路径不同,本文单独排查。

DeepSeek Harness 工具调用后崩溃长什么样

报错固定为 Cannot read properties of undefined (reading 'prepare')、code UNKNOWN,出现在工具调用之后,且纯文本回合完全正常。 报告者实跑复现(讨论原文),现象如下:

  1. 会话日志序列:tool/call 发出后紧跟 step/end从未写出 tool/result,随后 turn/endreason.error "Cannot read properties of undefined (reading 'prepare')"
  2. 连续多轮复现:原帖 3 连崩(turn 14/15/16),后续复现扩展到 10 个连续 turn 崩溃;
  3. 纯文本 turn 正常(无工具时 turn/end completed)——说明模型/provider 链路健康,故障集中在工具分发层;
  4. 重启/resume 后会话即恢复reason:"resume"),服务层从未 crash(curl :3080 长期 HTTP 200)。

这条证据链有个关键判别点:会话早期(turn 1–13)每个 tool/call 都有配对 tool/result,说明调度器当时是活的;如果 profile 里静态躺着重复核心包,第一个工具调用就该崩——中途才开始崩,指向"进程生命周期内模块图被改动"或"另一条求值边"。

版本漂移与重复核心包:同源不同路径

两者共享同一机制——TOOL_RUNTIME_SCHEDULER 是普通 Symbol() 而非 Symbol.for()定义处,实测 lib/index.js:2416),每次调用生成唯一实例,两份副本的键永远不相等,跨份读取 registry[TOOL_RUNTIME_SCHEDULER] 就是 undefined(#4529 根因确认)。 但触发载体不同:

  • 静态重复#4640):profile 里躺着一棵残留的 node_modules/@deepseek-ai/ 树,启动后第一个工具调用就崩;
  • 版本漂移(本文):插件把核心包当直接依赖装进 profile,解析到旧版本0.1.0-rc.8 ≠ 全局 0.1.1-rc.2),会话中途开始崩(turn 14 起),重启/resume 或禁用该插件后恢复。

报告者实测确认:profile node_modules/@deepseek-ai/dsh-tools实体目录、非 symlink、版本 0.1.0-rc.8,与全局 CLI 内 0.1.1-rc.2lib/index.js:2416 字符串相同但 Symbol 实例不同——两条物理文件构成两条求值边。

谁把旧版核心包拉进 profile:直接依赖名单

@deepseek-ai/dsh-tools 声明为 dependencies(而非 peerDependencies)的第三方插件,就是注入源。 报告者逐包核对(来源):

  1. dsh-obsidian"@deepseek-ai/dsh-tools": "*"(通配符最危险,永远装最新但可能漂移);
  2. dsh-better-sidebar"^0.1.0-rc.8"
  3. dsh-chat-importdsh-recall"^0.1.0-rc.8" 系;实测名单里是 "^0.1.0-rc.6"
  4. dsh-univer-office"0.1.0-rc.8"固定旧版本,直接把旧核心包钉进 profile)。

pnpm 解析到 0.1.0-rc.8(旧于全局 0.1.1-rc.2)就构成版本漂移的第二条求值边。另有旁路:npm install <plugin>(npm ≥7 会自动安装插件的 peer set)也会复制核心包,而 dsh plugin(pnpm、autoInstallPeers: false)不会——同一批插件,安装通道不同结果不同。

排查步骤:对比核心包版本 + 二分禁用插件

定位思路两条:先对比 profile 与全局的核心包版本,再用禁用插件做二分。 按步骤操作:

  1. 确认模型/provider 健康:让 Agent 发一段纯文本回复,正常即排除 provider 问题;再用 curl 确认服务层 200:
bash
curl -s -o /dev/null -w "HTTP %{http_code}\n" http://127.0.0.1:3080/
  1. 排除静态重复:开一个全新会话跑第一个工具调用——首轮就崩走重复核心包排查,中途崩继续本文;
  2. 对比版本:先看全局 CLI 版本(dsh --version),再列出 profile 里的核心包,逐个比对:
bash
dsh --version
ls ~/.dsh/profiles/<name>/node_modules/@deepseek-ai/

profile 里是旧版本(如 0.1.0-rc.8、全局 0.1.1-rc.2)即漂移; 4. 找物理副本:用 find 确认 profile 内是否有实体目录:

bash
find ~/.dsh ~/.npm-global -type d -name dsh-tools 2>/dev/null
  1. 二分禁用插件:列出已装插件,把可疑第三方插件在 profile 配置里设为 disabled: true
bash
dsh plugin --profile <name> list

重启 dsh web,新会话工具全部正常(bash/read/glob 均返回结果)即锁定罪魁; 6. 彻底恢复:卸载该插件 + 移走 profile 内版本漂移的核心包副本,重启后新会话工具恢复;已遗留 tool_calls 的旧会话放弃。

报告者实测:禁用第三方视觉插件并重启后,新会话工具全部恢复正常——该插件的 enable/disable 就是"能否稳定复现"的开关,可据此做 A/B 验证。

修复与预防:Symbol.for 与插件依赖规范

修复方向分两层:官方把调度器键改成跨副本一致的 Symbol.for(),插件作者把核心包放 peerDependencies 社区给出的建议(来源):

  1. 键改 Symbol.for('@deepseek-ai/dsh-tools.scheduler'):两份副本算出同一个键,崩溃降级为"仅冗余安装";
  2. 读取处显式判空registry[TOOL_RUNTIME_SCHEDULER] 命中 undefined 时抛可行动诊断(点名本机可能的重复/漂移来源),而不是裸 UNKNOWN TypeError——本次排查正是因为错误被统一抹平成 UNKNOWN 才花了大量人时;
  3. 插件作者契约@deepseek-ai/* 一律放 peerDependencies,严禁写进 dependencies"*" 通配符和固定旧版本尤其要避免;
  4. 诊断配方:崩溃在 web 会话 log 内、不落 stderr,解析 session.jsonlturn/end error,配合 turn_summary.py 逐 turn 统计 tool/calltool/result 配对即可快速定位。

在官方修复落地前,按本文步骤禁用/卸载注入源即可恢复。这类问题说明插件依赖声明规范比功能更重要——如果你不想逐个核对依赖树,安装目录里经过人工验证的插件会少很多这类打包问题。DeepSeek Harness 桌面端内置的 DSH Plugin Hubdsh-plugin.org)集中展示插件来源与已装清单,禁用、卸载一目了然,避免手改 profile 依赖。

注意事项

  1. 崩溃 turn 后遗留 tool_calls 无结果,该会话可能被模型提供方以 400 INVALID_REQUEST 拒绝(#4549 恢复缺口),别在同一会话里反复重试。
  2. 禁用插件 + 重启是"两个变量同时变",若要严谨归因,先只重启不重启插件、再只禁用不重启,分开验证。
  3. 崩溃当时 profile 是否已含漂移副本,报告者承认无法回溯确认——若你的场景需要铁证,在崩溃前/后的干净环境做 A/B。
  4. 同类插件报错还汇总在《DeepSeek Harness 插件报错合集:DSH plugin 不加载、Web UI 异常与会话缓存修复》里。

来源:Discussion #4601#4529(根因确认)packages/core/toolspackages/core/agent-loop

常见问题

DeepSeek Harness 会话中途工具调用后崩溃报 Cannot read properties of undefined (reading 'prepare') 是什么原因?

第三方插件把 @deepseek-ai/dsh-tools 声明为直接依赖(dependencies 而非 peerDependencies),安装进 profile 时解析到旧版本(实测 0.1.0-rc.8,晚于全局 CLI 的 0.1.1-rc.2)。两份核心包各自用普通 Symbol() 做调度器键、互不相等,agent loop 跨份读取时取到 undefined(来源)。

这个 reading 'prepare' 报错和『重复核心包』有什么区别?

机制同源(Symbol() 对重复加载不健壮),但触发路径不同:静态重复是 profile 里残留整棵副本、启动后第一个工具调用就崩;版本漂移是插件把旧版核心包当直接依赖装进 profile,会话中途(如第 14 轮)才开始崩,且重启/resume 或禁用该插件后即恢复。

怎么用命令确认是版本漂移而不是静态重复核心包?

三步:开全新会话看第一个工具调用是否崩(崩=静态重复,参考另一篇排查);对比 profile 与全局核心包版本(dsh --version 看全局,ls ~/.dsh/profiles/<name>/node_modules/@deepseek-ai/ 看 profile);再跑 find ~/.dsh ~/.npm-global -type d -name dsh-tools 2>/dev/null 找物理副本。

哪些第三方插件会把旧版 @deepseek-ai/dsh-tools 核心包拉进 profile?

把 @deepseek-ai/dsh-tools 写进 dependencies 的插件都会。实测名单:dsh-obsidian("*")、dsh-better-sidebar("^0.1.0-rc.8")、dsh-chat-import / dsh-recall("^0.1.0-rc.6")、dsh-univer-office(固定 "0.1.0-rc.8" 旧版)。npm ≥7 还会自动安装插件的 peer set 复制核心包,dsh plugin(pnpm,autoInstallPeers: false)不会。

怎么恢复被版本漂移搞崩的会话?禁用插件和清理副本的步骤?

先禁用可疑第三方插件(profile 里 disabled: true)重启 dsh web 验证工具恢复,再彻底卸载该插件并清理 profile 内版本漂移的核心包副本;崩溃 turn 遗留 tool_calls 无结果时,该会话可能被模型提供方以 400 INVALID_REQUEST 拒绝,只能开新会话。

来源