2095 字
10 分钟
DSH插件市场更新报'already-installed',一次ai完整的排查与修复

一句话结论#

修法有三种,按推荐顺序:

  1. 把市场升级到 1.66.37+,之后市场更新别的插件会自动走「先移除再安装」;
  2. 市场自己,从桌面端 设置 → 插件 里更新;
  3. 完全退出 DSH,用 pnpm 直接改 profile(本文最终采用,最可控)。

现象#

在插件市场点任意插件的「更新」,都会得到同一句红字:

插件市场报错
更新失败:dshmarket —— dshmarket 更新失败,且更新前的构建未能验证恢复
({"code":"already-installed"});请先检查该 profile,再重新启动。

「全部更新」也一样。市场里四个插件(dshmarket、better-glob、tools-manager、以及当时的 codex-ui)无一例外。

更新失败提示

先确认:profile 到底坏没坏#

三个地方都看一遍,结论是一个字节都没动:

检查点结果
profiles\desktop\package.json仍是 ^1.66.32 / ^0.2.1 / ^0.4.1
profiles\desktop\pnpm-lock.yaml仍是 dshmarket@1.66.32 等旧版本
profiles\desktop\node_modules\<pkg>\package.json版本没变
.dsh-market\state.json没有 brokenPlugins 记录

定位过程#

1. 市场自带持久日志#

市场把每个操作都写进 .dsh-market\log.ndjson(就是界面上「导出日志」的来源)。关键三行:

.dsh-market/log.ndjson
// 10-17,还能更新
{"at":"2026-10-17T07:53:42.793Z","level":"info","event":"update","detail":"dshmarket -> dshmarket@1.66.32 exit=0"}
// 10-18 起,全部失败
{"at":"2026-10-18T08:08:44.259Z","level":"error","event":"update","detail":"dshmarket -> dshmarket@1.66.37 exit=1 err={\"code\":\"already-installed\"}"}
{"at":"2026-10-18T08:08:44.259Z","level":"error","event":"update-rollback","detail":"dshmarket: failed update command and restoration of the previous build could not be verified — {\"code\":\"already-installed\"}"}

注意:update 事件自己就是 exit=1,不是只有回滚失败。说明第一次 add 就被拒了,already-installed 不是回滚产生的次生错误。

为什么时间线很关键

10-17 更新成功,10-18 早上应用升到 0.2.1-rc.2 之后,所有更新开始失败。~\.dsh\dsh-runtimes\dsh-primary-runtime\runtime.json 里还写着 0.2.0-rc.2,正好是刚升级过的痕迹。

所以这不是「某天突然坏了」,而是宿主版本变化带来的行为变化。

2. 源码里只有一处抛这个错误码#

already-installed 是 ManagementFailure 的 code,被 managementError() 序列化成 {"code":"already-installed"} 打到 stderr。整个运行时只有一处抛出它——@deepseek-ai/dsh-plugin-manager/lib/index.js:1991:

dsh-plugin-manager/lib/index.js
const before = readProfileManifest("dsh", this.profile.dir).dependencies ?? {}
const known = specPackageName(spec, before, this.profile.dir)
if (this.ownerContext.get("hmr") !== void 0 && known !== void 0 && Object.hasOwn(before, known))
throw new ManagementFailure("already-installed")

翻译过来就是:

只要 HMR 服务在(也就是 DSH 正在运行),对一个已经写在 profile dependencies 里的包执行 add,一律拒绝。

更新已安装的插件正好是「对已存在的依赖 add」;而全新安装一个没装过的插件不受影响(这也解释了为什么装新插件一直正常)。

flowchart TD
    A["点「更新」<br/>add pkg@version"] --> B{"包已在<br/>dependencies 里?"}
    B -- 否 --> C["installBundle 正常执行 pnpm"]
    B -- 是 --> D{"HMR 服务存在?<br/>即 DSH 正在运行"}
    D -- 否 --> C
    D -- 是 --> E["already-installed<br/>pnpm 从未启动"]

3. 为什么提示是「更新前的构建未能验证恢复」#

为什么 dsh plugin --profile desktop add 也不行#

CLI 的启动器里写死了:

apps/cli/lib/bin.js
function rejectElectronProfile(program, profile) {
if (profile.toLowerCase() === 'desktop')
program.error('error: profile "desktop" is managed exclusively by the Electron application')
}

市场源码里的注释补充得更细:rc.2 起,只有桌面端自己的载体在完全退出后才接受这个 profile(它会拿 120 秒的 package.json 锁)。所以「在 DSH 里跑 CLI 去改 desktop profile」这条路走不通。

三种解法#

A · 升级市场

把市场升到 1.66.37+。它新增了 installOrReinstall():遇到 already-installed 就自动改成「先 removeBundle,再 install」——官方插件页更新 bundle 也是这两步,因为管理器本来就没有原地更新。

dshmarket/src/official-desktop.ts
try {
first = await manager.installBundle(spec, options)
} catch (error) {
if (managementCode(error) !== 'already-installed') throw error
first = { application: 'failed', error }
}
if (first.application !== 'failed' || managementCode(first.error) !== 'already-installed') return first
const name = registryNameOf(spec)
const removed = await manager.removeBundle(name) // 先移除
if (removed.application === 'failed' || removed.application === 'cancelled') return removed
return install() // 再安装

代价:市场自己不能这样自更新(issue #900)。卸载会把「正在执行安装的这段代码」一起摘掉,补装那一步永远执行不到。所以市场自己的升级仍然要走 B 或 C。

B · 桌面端插件页

官方入口,最省事:在应用的 设置 → 插件 里移除再安装市场。这个页面在市场之外执行同样的两步,不会把自己摘掉。

适合只想更新市场本身、不折腾命令行的场景。

C · 退出后用 pnpm

适合「一次更新好几个」「想要可重复、可核对」的场景。DSH 完全退出后,pnpm 不再受宿主那道守卫约束。

PowerShell(DSH 已退出)
cd $env:USERPROFILE\.dsh\profiles\desktop
$r = "$env:USERPROFILE\.dsh\dsh-runtimes\dsh-primary-runtime\dependencies"
& "$r\node\bin\node.exe" "$r\pnpm\bin\pnpm.cjs" add `
dshmarket@1.66.37 `
'@huanlin/dsh-plugin-better-glob@0.2.2' `
'@huanlin/dsh-plugin-tools-manager@0.4.2'

两个真实的坑(都是 PowerShell 的)#

坑 1:PowerShell 5.1 默认不是 UTF-8#

而 if ($busy) { 实际在第 33 行。原因是 5.1 按系统 ANSI 代码页(简体中文是 GBK)读无 BOM 的脚本:UTF-8 的 3 字节汉字被当成 GBK 双字节后,多出来的字节会吃掉后面的换行/引号/括号,几行代码被并进注释里,大括号自然对不上。

同一个原因:UTF-8 的 package.json 被按 GBK 读,中文描述里的 " 被吃掉了。

坑 2:pnpm 用的是「当前目录」#

第一版脚本忘了切目录,pnpm add 在 C:\Users\<you> 里跑,于是在用户主目录建出了 package.json、pnpm-lock.yaml、pnpm-workspace.yaml、node_modules(10 个包)——而 profile 一点没变,脚本还”成功”了。

顺带:另一个会把 Agent 终端搞瘫的问题#

排查过程中 bash / pwsh 全部失败:

DSH 终端工具报错
DSH workspace preparation failed (not a user permission denial):
无法加载文件 ...\dsh-ops\bin\prepare-workspace.ps1,因为在此系统上禁止运行脚本

dsh-ops 插件在每次执行命令前会调用:

dsh-ops/lib/workspace-preparation.js
spawn('...\System32\WindowsPowerShell\v1.0\powershell.exe',
['-NoProfile', '-NonInteractive', '-File', script, ...])

没有带 -ExecutionPolicy Bypass,而机器执行策略是 Restricted,于是所有终端类工具直接不可用(文件读写类工具不受影响)。

修法(不需要管理员):

PowerShell
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned

验证清单#

更新后的验证

  1. 确认依赖范围 看 package.json 里三个包是否已变成新版本。
  2. 确认实际安装版本 看 node_modules\<pkg>\package.json,而不是只看依赖声明。
  3. 确认锁文件 pnpm-lock.yaml 里应出现 dshmarket@1.66.37 等新条目。
PowerShell
$p = "$env:USERPROFILE\.dsh\profiles\desktop"
# 1. 依赖范围
Get-Content "$p\package.json" -Raw -Encoding UTF8
# 2. 实际安装的版本
foreach ($n in 'dshmarket','@huanlin/dsh-plugin-better-glob','@huanlin/dsh-plugin-tools-manager') {
"{0} {1}" -f $n, (Get-Content "$p\node_modules\$n\package.json" -Raw -Encoding UTF8 | ConvertFrom-Json).version
}
# 3. 锁文件
Select-String -Path "$p\pnpm-lock.yaml" -Pattern 'dshmarket@|better-glob@|tools-manager@'

然后启动 DSH:市场里对应卡片的「更新」角标消失,插件正常加载、无 Loader 报错,即完成。

速查#

~/.dsh/profiles/desktop
  • .dsh-market
    • log.ndjson
    • state.json
  • .plugin-manager
    • logs
  • node_modules
  • package.json
  • pnpm-lock.yaml
  • pnpm-workspace.yaml
项路径
插件管理器源码<DSH 安装目录>\resources\app.asar\dsh\node_modules\@deepseek-ai\dsh-plugin-manager\lib\index.js
自带 node / pnpm%USERPROFILE%\.dsh\dsh-runtimes\dsh-primary-runtime\dependencies\{node,pnpm}

参考#

  • 拒绝逻辑:@deepseek-ai/dsh-plugin-manager → installBundle() 中的 HMR 守卫(already-installed)
  • 市场侧适配:dshmarket 1.66.37 src/official-desktop.ts(installOrReinstall、REFUSAL_COPY)
  • 相关 issue 编号(来自市场源码注释):#896(0.2.1 把 add 已存在依赖判为 already-installed)、#897(拒绝码被包进”未能验证恢复”)、#900(市场不能自更新)
DeepSeek HarnessDeepSeek HarnessDeepSeek HarnessHot Module ReplacementDeepSeek HarnessDeepSeek HarnessDeepSeek HarnessDeepSeek HarnessDeepSeek HarnessHot Module Replacement
DSH插件市场更新报'already-installed',一次ai完整的排查与修复
https://quqawsl.l.cd/posts/dshmarket-error/blog-dsh-market-already-installed/
作者
QuQawsl
发布于
2026-10-18
许可协议
CC BY-NC-SA 4.0

分享文章

生成精美分享图或复制链接,与更多人分享本文。

继续阅读

换条路线

从其他文章中稳定抽取