DSH plugin 启动报 HTML did not preload client.js?模块预载不变量排查

故障排查发布于 2026-09-12作者: DeepSeek Plugin 插件市场
DeepSeek HarnessDSH pluginclient-modules模块预载构建产物
启动后界面只显示 Failed to load plugins,控制台报 client-modules: HTML did not preload client.js?这不是 npm 包缺失,而是浏览器启动不变量失败。

如果你的页面只显示 HARNESS / Failed to load plugins,控制台报 client-modules: HTML did not preload @deepseek-ai/dsh-client-modules/client.js,先不要急着重装依赖。 这句话说的是一条精确的浏览器启动不变量失败了:页面里的 window.__ModuleLoader__ facade 已经运行并调用了 create(),但 registration queue 里没有 @deepseek-ai/dsh-client-modules 的 factory。问题在「队列里少了那个 factory」,而不一定在「磁盘上少了那个包」(#4836)。

DSH plugin 现象:Failed to load plugins 与 did not preload 的确切含义

这条报错的价值在于它非常精确——它只描述一件事:调用发生得比 factory 注册更早。 具体表现:

  1. 界面与栈都很短:页面显示 HARNESSFailed to load plugins,控制台只有这一条:
js
Error: client-modules: HTML did not preload @deepseek-ai/dsh-client-modules/client.js
    at Object.create ((index):13:39)
    at Cp.run (index.js:188:32)

注意 (index):13:39——报错发生在内联的 index 脚本里,而不是某个打包产物内部,这本身就说明失败点在页面装配阶段(#4836)。 2. 它不是「包缺失」的同义词__ModuleLoader__ facade 已经运行、create() 也已经调用,只是它的 registration queue 里没有目标 factory。因此「包在磁盘上是否存在」与「队列里是否注册了」是两个不同的检查点,报错只指向后者(#4836)。 3. 可能的失败面比想象中宽:HTML 注入内容不完整或顺序错乱、/plugins 路由返回的不是 JavaScript(404、登录页、代理错误页都算)、反向代理/CDN 改写了 ??rev 查询串、CSP 或 TLS 或浏览器扩展或 service worker 拦截脚本、脚本被加上 async / defer / type=module、仅公网入口失败而 localhost 直连正常——这些都会以同一条报错表现出来(#4836)。 4. 最容易踩的前提错误:验证环境不对。动态 /plugins 路由与 HTML 注入都来自 Host plugin,而 apps/web 自己的 package contract 明确说明它的 dist 由 CLI 的 dsh web 提供。所以裸 apps/web 的 Vite 开发服务、IDE 预览、或者只把静态 dist/ 复制出来的做法,都不承担这条启动契约,出现这个报错也就不奇怪(#4836)。 5. 社区侧的挫败感值得警惕:有人折腾半天后暂时放弃了这个工具链,等有空再折腾——这提示这条报错如果只靠「重装」很难收敛,必须按证据排查(#4836)。

DSH plugin 机制:浏览器启动不变量——queue、blocking bootstrap、boot graph 的顺序契约

这条不变量之所以精确,是因为它有明确的步骤与次序;理解次序,就知道该在哪些点取证。 逐层拆解:

  1. alpha.1 的正确启动顺序是五步:① 内联 __ModuleLoader__ queue;② application preload links;③ parser-blocking/plugins/??...&rev=... bootstrap script;④ 内联 __DSH_BOOT__ graph;⑤ Web shell 调用 create()。任何一步缺失、内容不对或顺序被打乱,第 ⑤ 步就会在队列里找不到 factory(#4836)。
  2. 第 ③ 步必须是 parser-blocking:一旦脚本被加上 async / defer / type=modulecreate() 就有机会先跑——那正是「facade 已运行而队列为空」的形态。所以这一项要作为独立检查点(#4836)。
  3. rev 是版本一致性的锚:HTML 里注入的 rev 与实际 /plugins/??...&rev=... 返回的构建产物必须来自同一次构建。因此逆向代理或 CDN 若吞掉/改写了 ??(多路合并请求)或完整 query,就会让浏览器拿到与 HTML 不匹配的产物,而报错仍然是这一条(#4836)。
  4. 取证必须来自同一次页面加载:不要把多次重启后的证据混在一起。这正是很多人「查了半天没结论」的原因——两份证据来自不同构建,于是要么都正常、要么都异常,都不反映真实故障(#4836)。
  5. 报错本身不区分原因:拦截(CSP / TLS / 扩展 / service worker)、路由错误(404 或返回 HTML)、代理改写(query 丢失)、装配问题(缺少或乱序)、环境错误(不是 Host composition),最终都收敛成同一句话。所以它是一条断言而不是一份诊断——诊断要靠证据补齐(#4836)。

DSH plugin 排查与恢复:同一次页面加载取证、逐项排除与已验证的修复路径

按顺序取证比反复重装有效得多;先固定一份可信证据,再逐项排除。 具体做法:

  1. 第一步:从同一次页面加载同时取 HTML 与 bootstrap 响应。先把 HTML 存下来,再从 HTML 里原样复制 bootstrap URL:
bash
curl -fsS http://127.0.0.1:端口/ -o dsh-index.html
grep -o '/plugins/[^"<]*' dsh-index.html
grep -n '__ModuleLoader__\|__DSH_BOOT__\|script src=' dsh-index.html

然后用 curl -i 请求那个原样复制的 bootstrap URL(#4836)。 2. 第二步:逐项核对以下检查点——① HTML 是否真的包含 queue、blocking bootstrap script 与 boot graph,且顺序正确;② /plugins 是否返回 200 的 JavaScript,而不是 404、登录页或代理错误 HTML;③ 代理/CDN 是否保留 ?? 与完整 query/rev;④ 是否被 CSP、TLS、浏览器扩展或 service worker 拦截;⑤ 是否给 script 加了 async / defer / type=module;⑥ localhost 直连是否正常、是否只有公网代理失败;⑦ 启动的是否为 CLI 的 dsh web Host composition,而不是裸 apps/web Vite、IDE preview 或只复制出来的静态 dist/#4836)。 3. 第三步:如果要把问题交给别人定位,按清单补齐信息——启动命令与 cwd、版本或 commit、Node/pnpm 版本、页面 URL、HTML 中的 bootstrap URL、该 URL 的 status 与 content-type、浏览器 Network 面板里的失败原因,以及 Host activation / fiber 日志。补齐这些,根因就能被缩到一个边界上(#4836)。 4. 第四步:社区中反复出现的可用恢复路径是「Node 版本 + 重新构建」。具体包括:切换到 Node 22 后正常;升级到 Node v24.20.0 并重新构建后正常使用;也有人通过自行改动代码解决。由于原始报告并未确认单一根因,这里把它们作为已验证的恢复路径列出,而不是当作唯一解释——如果你的排查指向构建产物不一致,这条路径尤其值得先试(#4836)。 5. 第五步:不要绕过断言。这条报错的检查点是安全的:它宁可让插件树加载失败,也不让页面在模块不完整的状态下继续跑。绕过它只会把「明确的失败」换成「行为不确定」。社区整理的排查手册也明确以「不绕过该断言」为前提来组织安全恢复顺序(#4836)。 6. 给插件作者的启示:这条不变量正说明前端插件的加载契约依赖装配顺序与版本一致性。当你在 DSH Plugin Hub 上分发 DSH插件、尤其是带前端构建产物的 DeepSeek插件 时,请确保构建产物与宿主注入的 rev 一致,并且不要在宿主脚本上做 async / defer 之类的加速改动——那会直接破坏这条契约(#4836)。

DSH plugin 排查注意事项

先记住这条报错是断言而不是诊断——它只告诉你「调用发生在 factory 注册之前」,至于为什么早,必须靠同一次页面加载的证据来回答。 八条要点:

  1. 别先重装:这条报错描述的是队列里缺 factory,不一定是包缺失。
  2. 取证要在同一次页面加载内完成:混用多次重启的证据会得出错误结论。
  3. rev 必须一致:HTML 与 /plugins 产物要来自同一次构建。
  4. 确认是 Host composition:裸 apps/web Vite、IDE 预览、静态 dist/ 都不承担这条契约。
  5. 检查代理是否吞掉 ??:多路合并请求的 query 不能丢。
  6. 脚本必须 parser-blockingasync / defer / type=module 会破坏顺序。
  7. 先试 Node 版本 + 重新构建:这是社区反复验证的恢复路径。
  8. 不要绕过断言:它保护的是「模块完整才继续跑」这一前提。
DSH Plugin Hub 插件市场:重新获取带前端构建产物的插件并核对版本

来源:Discussion #4836Discussion #4885web-client-plugin-boot-failure runbook

常见问题

DSH plugin 启动报这个错误,是说 npm 包缺了吗,我到底该重新安装什么?

DSH plugin 启动时报这条错,通常不是 npm 包缺失,而是一条**精确的浏览器启动不变量**失败了。这条报错表达的是:页面里的 window.__ModuleLoader__ facade 已经运行并调用了 create(),但 registration queue 里**没有** @deepseek-ai/dsh-client-modules 的 factory。也就是说问题在「队列里少了那个 factory」,而不一定在「磁盘上少了那个包」——所以先别急着重装,按顺序取证才能定位(来源:Discussion #4836)。

DeepSeek Harness 插件预载失败时,为何必须从同一次页面加载保存 HTML 和 bootstrap 响应?

DeepSeek Harness 的这条预载不变量依赖**版本一致**,所以 HTML 与 bootstrap 响应必须来自同一次页面加载。HTML 里注入的 rev/plugins/??...&rev=... 实际返回的构建产物必须来自同一次构建。如果你先重启服务、再分别抓 HTML 和脚本,两份证据可能来自不同的构建,于是「明明都正常」和「明明都错」都会出现,而它们都不反映真实故障。取证方法就是一次 curl 存 HTML、从 HTML 里原样复制 bootstrap URL、立刻 curl -i 它(来源:Discussion #4836)。

为什么不能用 IDE 预览或直接打开 dist/ 来验证 DSH plugin 的启动问题?

DSH plugin 的动态 /plugins 路由与 HTML 注入都来自 Host plugin,所以在错误的验证环境里必然复现这条报错。apps/web 自己的 package contract 明确说明它的 dist 由 CLI 的 dsh web 提供,而不是自己起服务。所以你必须是 CLI 的 dsh web Host composition 在跑;裸 apps/web 的 Vite、IDE preview、或只把静态 dist/ 复制出来的做法,都无法满足这条启动契约,看到这个报错也就毫不意外(来源:Discussion #4836)。

社区里实际是怎么把这个 DeepSeek Harness 插件启动问题修好的?

社区里验证过的 DeepSeek Harness 插件恢复路径,集中在「Node 版本 + 重新构建」这一组。有人切换到 Node 22 后恢复正常;有人把 Node 升级到 v24.20.0 并重新构建后正常使用;也有人通过自行改动代码解决。需要说明的是,这些是**用户侧已验证的恢复路径**,报告本身并未确认单一根因——所以正文按「先取证、再按证据缩小边界」来组织,而不是先断言原因(来源:Discussion #4836)。

相关术语

preload invariant(预载不变量)
preload invariant 是 Web shell 调用 create() 之前页面必须按顺序完成的四步装配契约:inline __ModuleLoader__ queue → application preload links → parser-blocking /plugins bootstrap script → inline __DSH_BOOT__ graph。缺任何一步或顺序被打乱,create() 就会在队列里找不到对应 factory。https://github.com/deepseek-ai/deepseek-harness/discussions/4836
__ModuleLoader__ queue
页面内联的模块加载器 facade 及其 registration queue。它先于所有应用脚本运行;当 facade 已经 create() 而队列里没有目标 factory 时,就抛出 did not preload 这条错误。https://github.com/deepseek-ai/deepseek-harness/discussions/4836
Host composition
Host composition 是由 CLI 的 dsh web 提供的宿主侧组合,负责注入 HTML、提供动态 /plugins 路由并挂载 Host plugin。它与裸 apps/web 的 Vite 开发服务不是一回事,后者不承担这条启动契约。https://github.com/deepseek-ai/deepseek-harness/discussions/4836

来源