DSH 报错 “Can’t find variable: Iterator” 的完整排查与解决(VPS + Tailscale + Safari 真实案例)

摘要

一次真实的排障记录:从一个误导性的 openclaw: command not found 开始,经过 Tailscale、pnpm 依赖、DSH 配置文件、插件冲突等一连串弯路,最终发现根因只是客户端浏览器太旧。本文把所有踩过的坑都记录下来,帮你少走弯路。

TL;DR(先说结论)

如果你从 Safari 访问 DeepSeek Harness(DSH)时看到:

Failed to load plugins
failed to import loader entry ... (@deepseek-ai/dsh-client-ui-sidebar-documentpreview): Can't find variable: Iterator

问题不在服务器,而在你的浏览器。换成 Chrome(或升级到 Safari 18.4+ / macOS Sequoia 15.4+)即可解决。Iterator 全局对象只在 Chrome 122+ 和 Safari 18.4+ 中才原生支持。

事情是这样的

这最初是一个「OpenClaw 不工作」的报障。最后发现是浏览器兼容性问题。下面是完整的排查路径,包括走错的路——你可以直接跳过它们。

第一步:误导性的第一个错误

最初的症状是:

root@vps:~# openclaw gateway restart
コマンド 'openclaw' が見つかりません。

openclaw: command not found(找不到命令)。看起来很简单,但它把我们带偏了一段时间。用户实际运行的是 DeepSeek Harness(DSH),不是 OpenClaw。这是两个不同的工具。排障前永远先确认你要排查的软件到底是哪个。

第二步:先查 Tailscale

在动应用层之前,先确认网络层:

tailscale status

VPS 上一切正常。但 Mac 客户端显示:

100.98.125.115  binmacbook-pro  offline, last seen 19m ago

Mac 上的 Tailscale 已经停了。教训:如果你从笔记本访问不到服务,先查笔记本自己的网络状态——而不是服务器的。

第三步:让 DSH 跑起来

网络通了之后,DSH 本身却启动不了。错误一个接一个:

错误 A:缺失的 tarball 依赖

Failed to resolve dependency: Could not install from
"/tmp/dsh-llm-finish-reason-tolerance-0.2.0.tgz" as it does not exist.

某个插件是从 /tmp 里的本地 tarball 安装的,而 /tmp 会在重启后被清空。但 package.json 里还留着这个引用。修复方法:

# 从 package.json 里删掉这条死引用
sed -i '/dsh-llm-finish-reason-tolerance/d' package.json
pnpm install

经验法则:永远不要从 /tmp 安装任何东西。用 /root/plugins/ 这种持久路径。

错误 B:格式错误的 patch 文件

DSH 要求 cordis.patch.yml 必须是顶层 YAML 数组。只包含注释的文件会被解析成 null,直接崩溃:

Error: patches /root/.dsh/cordis.patch.yml must be a top-level YAML array

修复:写 [],或者干脆删掉这个文件。

错误 C:重复的 loader entry id

duplicate loader entry id: web-compat

同一个插件同时挂在根级 patch 和 profile patch 里。DSH 会合并所有 patch 层——id 在所有层之间必须唯一。修复:只在一层里挂载。

错误 D:服务名冲突

service "directoryPicker" has been registered at <host-web-compat>

dsh-host-web-compat 插件注册的 directoryPicker 服务和 DSH 内置的 @deepseek-ai/dsh-host-directory-picker-browse 撞名了。这个插件是为 Android WebView 和老旧内嵌浏览器设计的,不是给桌面 Safari 用的。删掉它才是正确的做法。

第四步:DSH 终于启动

清理完上面四个问题后,DSH 正常跑起来了:

dsh web: http://127.0.0.1:3080/?token=E0C0KKRi7GI8P8F1FmXKswZrZKTZUzgC1_yaX7NxPMs
LISTEN 0 511 127.0.0.1:3080 ...

第五步:真正的根因

但浏览器里依然显示:

Can't find variable: Iterator

关键点在这里:到最后一刻才有人去查浏览器本身。用户用的是 Safari。Iterator 全局对象只在 Safari 18.4 和 Chrome 122 里才被加入。旧版本打开时,加载 documentpreview 插件会直接抛这个错。

换成 Chrome 后立刻正常。

正确的排查顺序

如果可以重来,顺序应该是:

  1. 确认你实际运行的软件是什么(OpenClaw?DSH?还是别的)
  2. 检查客户端的网络状态(Tailscale、VPN、DNS)
  3. 确认服务端能干净启动(没有缺失依赖、没有格式错的配置)
  4. 检查浏览器版本——这一步最容易被忽略
  5. 最后才考虑兼容性补丁或变通方案

关键要点

  • openclaw: command not found 不代表 OpenClaw 坏了——它代表 OpenClaw 根本没装。先确认机器上到底有什么。
  • 笔记本上的 Tailscale「offline」通常是笔记本自己的问题,不是服务器的。
  • 永远不要从 /tmp 安装东西——重启会被清空,留下悬空引用,破坏后续的包管理操作。
  • DSH 的 patch 文件必须是顶层 YAML 数组——不能只有注释,也不能是对象。
  • Loader entry id 在所有 patch 层之间必须唯一——根级、profile 级、bundle 自带的 patch 都会合并。
  • dsh-host-web-compat 是给 Android WebView 用的,不是给桌面浏览器用的。别为了修桌面 Safari 去装它。
  • Iterator 报错是客户端浏览器兼容性问题。Chrome 122+ 或 Safari 18.4+ 即可解决。不需要、也不应该在服务端修。

真正有用的命令

让 DSH 常驻运行:

tmux new-session -d -s dsh \
  "/root/.nvm/versions/node/v24.18.0/bin/npx @deepseek-ai/dsh web --no-open 2>&1 | tee /tmp/dsh-web.log"
sleep 8
tmux capture-pane -t dsh -p -S -20 | grep -oE 'http://127\.0\.0\.1:3080/\?token=[A-Za-z0-9_-]+'

从 Mac 访问:

ssh -L 3080:127.0.0.1:3080 root@<vps-ip>

然后在 Chrome 里打开 token URL(不要用 Safari,除非你已经在 Safari 18.4+)。

结语

一场两小时的排障,最后以「换个浏览器」告终。教训不是「浏览器兼容性很简单」,而是——客户端侧的假设应该尽早验证,而不是最后才查。服务器没问题,网络没问题,浏览器才是整件事的全部。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注