# 06 · 故障排查 > 每条都给出**症状 → 根因(机制)→ 处置 → 验证**。标注 ✅ 的条目是在真机上定位并验证过的案例。 ## 速查表 | 症状 | 最可能根因 | 跳到 | | --- | --- | --- | | 装完「像没装」,界面无变化且**无任何日志** | peer 范围与宿主版本不匹配 → 静默禁用 | §1 | | 页面出现**两个侧边栏** / 两套开合按钮 | 双挂载(两条线同装,或残留挂载行) | §2 | | HTML 预览只显示 `forbidden` | 桌面壳渲染器围栏 + 不透明源 iframe | §3 | | 插件面板盖住宿主/第三方弹窗(如插件市场) | 面板宿主挂在层叠上下文之外 | §4 | | 终端打不开 / `node-pty` 加载失败 | pnpm 拦截原生构建(仅 v0.19.1 线) | §5 | | 改了代码,界面没变 | 产物未重建 / profile 副本未刷新 / 浏览器缓存 | §6 | | DSH 拒绝加载插件、entry pending | 插件声明(`dsh.plugin.json` / bundle patch)不合法 | §7 | | DSH 启动进**恢复模式** | profile 配置被写成错误类型(如 YAML 布尔) | §8 | --- ## §1 装完「像没装」:静默禁用 ✅ **症状**:profile 里依赖装了、文件也在,但界面没有任何变化;日志里也没有插件相关报错。 **根因**:宿主启动时做兼容性预检,判定 `peerDependencies` 与自身版本是否满足: ``` semver.satisfies(hostVersion, peerRange, { includePrerelease: true }) ``` caret 范围**跨 minor 必然为 false**(例如 `^0.1.5-rc.1` 对 `0.2.0`)。判定失败后宿主的处理是 **跳过这一行**——不抛错、不打日志、不加载。 **处置**:核对宿主版本,改用匹配的版本线(见 [04-部署指南](04-部署指南) §1.1)。 ```bash grep -m1 '"version"' "/Applications/DSH Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/dsh/package.json" grep -n 'dsh-client-ui-sidebar-right' <插件仓库>/package.json # 看 peer 范围 ``` **验证**:重启后右列出现插件 tab;`lib/client.js` 里能 grep 到 `apes:agents`。 **教训**:这一条没有日志可查,因此**部署前先看版本矩阵**是唯一可靠的预防手段。 ## §2 两个侧边栏 / 两套开合按钮 ✅ **症状**:窗口里出现两套右侧面板与两组开合控件。 **根因**:两个方向都可能: 1. **旧版遗留**:v0.18.0 基线时插件自绘右侧面板与开关(`toggleCluster`)。升级后自绘部分应已移除。 2. **双挂载**:同一条线被挂两次(profile 自己的 `cordis.patch.yml` 里还留着旧挂载行, 与 bundle patch 同时生效),或者本 fork 线与上游线同时安装(两者都注册 `ctx.betterSidebar`, entry id 冲突,第二个被丢弃)。 **处置**: ```bash # 确认产物里已经没有自绘开关 grep -c toggleCluster <插件仓库>/lib/client.js # 期望 0 # 检查是否双挂载:profile 里是否同时存在 bundle 行 与 手写挂载行 grep -n "dsh-apes-sidebar\|dsh-better-sidebar" ~/.dsh/profiles//cordis.patch.yml ~/.dsh/profiles//package.json ``` - 有残留手写挂载行 → 删除它,只保留一条通道。 - 两条线同装 → `bash scripts/switch.sh aps`(或 `upstream`)二选一。 - 装了聚合包(如 `@linxin666/dsh-web-ui-all`)→ 确认聚合包在本包之前,让包内守卫生效。 **验证**:右列只剩一套面板与一个开合控件。 ## §3 HTML 预览显示 `forbidden` ✅ **症状**:预览 HTML 文件时 iframe 里只有一行裸文本 `forbidden`;而「临时解锁」后又能看。 **根因(三层)**: 1. DSH Desktop 在**扩展模式**下给所有路由套了一层请求围栏:`ordinaryBrowserEnabled` 恒为 false, 不带 Electron 注入的 `x-dsh-desktop-renderer` 头的请求一律 `403 + "forbidden"`; 2. 该头只注入给 **mainFrame** 与**同源**子框架; 3. 预览 iframe 的 sandbox 令牌不含 `allow-same-origin`,请求时其源为 `null` → 拿不到头 → 403。 与「是否升级了插件基线」无关(v0.18.0 起行为一致)。 **处置**:插件已在 1.2.0+ 用 **`srcDoc` 交付**修复(主框架取文档 → 注入 `` → 注入 iframe), `main` 上对应提交 `4c242ac`。**不要**通过给 iframe 加 `allow-same-origin` 来"修"——那是安全降级。 **验证**: - 沙箱态下自包含 HTML(含内联脚本)能正常渲染; - 有外链资源的 HTML 在沙箱态下缺资源属**已知限制**,点「临时解锁」后应完整显示。 **排查命令**(确认宿主围栏在起作用): ```bash lsof -nP -iTCP -sTCP:LISTEN | grep -i dsh # 找宿主端口(每次启动可能不同) curl -s -o /dev/null -w '%{http_code}\n' "http://127.0.0.1:<端口>/" # 无渲染器头 → 403 ``` ## §4 插件面板盖住宿主/第三方弹窗 ✅ **症状**:打开「社区插件市场」(宿主自带 `dsh-community-market`)时,弹窗左下角被本插件的 文件面板整齐切掉一块。 **根因**:桌面壳的有框模式会给上游 `#root` 打标 `data-dsh-desktop-content-viewport` 并施加 `isolation: isolate`,使 `#root` 成为**层叠上下文**;宿主的 overlay 层(`.dshDesktopOverlay`, `z-index: 1000`)位于其中。插件面板宿主若挂在 `document.body`(该上下文之外)且是定位元素, 就会画在整个上下文之上,把 overlay 里的弹窗盖住。 **处置**:已修(`4422001`)——面板宿主改为挂进 `#root[data-dsh-desktop-content-viewport]`, 无标记时回退 `document.body`。**不要**通过调大 `z-index` 解决(25 的取义见 [02-系统架构](02-系统架构) §四;乱调会破坏与宿主其他层级的关系)。 **验证**:插件市场弹窗完整盖住本插件面板,同时本插件面板仍在对话内容之上。 ## §5 终端不可用 / `node-pty` 加载失败(仅 v0.19.1 线)✅ **症状**:终端面板报 shell 启动失败或提示 `node-pty` 加载失败;`pnpm install` 阶段报 "ignored build scripts"。 **根因**:pnpm 11 默认**拦截依赖的构建脚本**,`node-pty` 的原生二进制没被编译。 (`main` 依赖 `node-pty`;`sync/v0.24.1` 已移除该依赖,终端由宿主内置承担。) **处置**: ```bash cd ~/.dsh/profiles/ pnpm approve-builds # 选中 node-pty pnpm rebuild node-pty # 必要时 # ⌘Q 完整退出 DSH 再启动 ``` **验证**:终端能开出 shell;`node_modules/node-pty/build/Release/*.node` 存在。 ## §6 改了代码,界面没变 ✅ **症状**:源码已改、`pnpm build` 也跑了,但界面行为仍是旧的。 **根因(三个叠加因素)**: 1. 产物**非字节确定性**,且 profile 的 `file:` 依赖是**复制**语义 → profile 里仍是旧副本; 2. `lib/client.js` 是浏览器的 `window.__ModuleLoader__.load({...})` bundle,**不是 CJS/ESM**, 在 Node 里 `require` 会因 `window is not defined` 报错——因此不能用「在 Node 里试跑」来验证前端改动; 3. host 半的改动必须重启进程(client 半有热加载,host 半没有)。 **处置**: ```bash cd <插件仓库> && pnpm build cd ~/.dsh/profiles/ && pnpm install # 刷新 file: 副本 # ⌘Q 完整退出 DSH → 重启 ``` **验证**:比对两边产物 sha: ```bash shasum -a 256 <插件仓库>/lib/client.js \ ~/.dsh/profiles//node_modules/dsh-apes-sidebar/lib/client.js ``` ## §7 DSH 拒绝加载插件 / entry pending **症状**:插件目录存在,但宿主不加载,或 loader 报 entry pending / 恢复模式。 **根因**:插件的声明面不合法或不被接受,常见于: - `dsh.plugin.json` 的 `engines.dsh` 与宿主不符; - bundle patch / 挂载行的 `insert` 结构不合法(例如把声明写成宿主无法解析的形态); - 同 id 重复挂载(`duplicate loader entry id` / `duplicate prefix route`)。 **处置**: ```bash cat <插件仓库>/dsh.plugin.json # 检查 id / engines / main cat <插件仓库>/cordis.patch.yml # 检查 insert 结构与守卫表达式 grep -n "cordis.patch\|dsh-apes-sidebar" ~/.dsh/profiles//package.json ``` 确保只有一条挂载通道、`name` 与实际包名一致(本 fork 为 `dsh-apes-sidebar`), `engines.dsh` 与宿主版本相容。改完 `pnpm install` + **完整重启**。 **验证**:启动日志无 loader 报错;右列出现插件 tab。 ## §8 DSH 启动进「恢复模式」 **症状**:DSH 启动后进入恢复/安全模式,插件与侧边栏都不正常。 **根因**:profile 或应用配置被写成**错误类型**。典型是材质/透明度字段要求**字符串** (`"off"` / `"transparent"`),但被 YAML 序列化成了布尔 `false` —— 宿主按类型校验失败即进恢复模式。 **处置与验证**: ```bash python3 -c "import yaml;d=yaml.safe_load(open('$HOME/.dsh/settings.yaml'));v=d.get('macosMaterial');print(repr(v), type(v).__name__)" # 期望:'off' str(或 'transparent' str);若为 False bool → 改回字符串 ``` 改配置前**先备份**,改完**回读校验类型**(见 [05-运维手册](05-运维手册) §五)。 --- ## 附:定位手法(本次会话验证有效) 1. **先按用户可见文案 grep 找归属**:`grep -rl "<界面上的中文短语>" /lib /node_modules`。 若两边都搜不到,说明它来自**运行时下载/缓存**(例如市场插件),再按 `NSUserData` 缓存或 `desktop-market/state.json` 之类线索去找提供方。 2. **再读提供方自己的 CSS/DOM 结构**:拿到它声明的 z-index、定位方式与所在槽位 (如 `renderSlot("shell.overlay")`),据此判断是「它太高」还是「我们太高」。 3. **层叠问题先找 `isolation` / `transform` / `filter` / `contain`**:这些才是层叠上下文与包含块的真正来源, 比 z-index 数值更关键。 4. **像素证据要用原图量**:交给视觉模型的截图会被缩放(1920→1280),裁剪坐标 ≠ 屏幕坐标; 用 PIL 在原图上量边界、比对 RGB 剖面,再下结论。