欢迎光临
我们一直在努力

AI | dsh报错失败400: {“type“:“MissingSessionID“,“message“:“Error from provider (Console Go)

400 MissingSessionID 排障实录:OpenCode Go 会话路由、DSH 插件补丁与双环境落地

一句话摘要:OpenCode Go 要求每个推理请求携带稳定的 x-opencode-session 头用于路由与提示词缓存,而 DeepSeek Harness(DSH)≤ 0.1.2-rc.1 在所有适配器路径上都不发这个头;本地装一个按会话注入 header 的插件即可修复。但"装上了却没生效"往往不是插件的问题——DSH 存在多套互相独立的数据目录(DSH_HOME),插件可能装到了 Desktop 永远读不到的那一套里。


1. 问题现象:一请求就 400

在 DeepSeek Harness 里切换到 OpenCode Go 模型后,所有推理请求直接失败,返回:

400: {
"type": "MissingSessionID",
"message": "Error from provider (Console Go): Request is missing x-opencode-session and cannot be routed efficiently. Please see https://opencode.ai/docs/go/#where-can-i-use-it"
}

特征:

  • 不是凭据问题:API key 有效、模型列表能拉到(GET https://opencode.ai/zen/go/v1/models 正常);
  • 不是偶发:100% 复现,任何 opencode-go 模型(opencode-go/<model-id>)都失败;
  • 报错即指路:OpenCode Go 官方文档明确要求客户端"为每个会话发送稳定的 x-opencode-session"(见 Where can I use it?)。

2. 背景分析:为什么必须带这个头?

2.1 OpenCode Go 是什么

OpenCode Go 是 OpenCode 推出的 $10/月的订阅,以接近批发成本的价格提供一批经过选型与压测的开源编码模型(Grok、GLM、Kimi、Qwen、DeepSeek V4、MiMo、MiniMax 等),主要面向国际用户并提供稳定访问。它本质上是"又一个 provider",只是对请求形态有约束:

  • 发送典型的编码 agent 流量;
  • 用自己的 UA 标识客户端(而不是通用 SDK/HTTP 库名);
  • 每个会话发送稳定的 x-opencode-session 会话 ID,用于路由与提示词缓存优化。
  • 2.2 缺失 session id 的两种代价

    类型表现代价
    显式失败 不带 header → 400 MissingSessionID 全部请求不可用
    隐式劣化 带了但 id 不稳定/全局共用 无报错,但路由不优、提示词缓存全部落空

    第二种情况下钱是实打实多花的:以 DeepSeek V4 Flash 为例,官方价格 输入 $0.22/1M、缓存读取仅 $0.007/1M(约 3%);GLM-5.3-Flash 输入 $0.15 vs 缓存读取 $0.03。会话 id 不稳定 = 每次请求都按全价输入计费。所以"把 id 修对"不只是过网关,还直接影响成本与延迟。

    2.3 为什么 DSH 会缺这个头

    OpenCode 官方文档的 Known Problematic Clients 表里赫然列着:

    DeepSeek Harness — Session information arrives on some model paths, but is missing on others. We recognize its native header; the remaining work is to send it across all adapters. Discussion #5495

    即:DSH 部分模型路径带会话信息、其他路径不带;上游已识别 DSH 原生头,但尚未在所有适配器路径统一发送(社区插件作者记录为 2026-09-05 起 OpenCode Go 开始强制执行,而 DSH ≤ 0.1.2-rc.1 未覆盖)。修复在 upstream 发布前,需要本地补丁。

    2.4 候选方案的取舍

    方案修复 400每会话独立 id(缓存/路由最优)不影响其他 provider免重启开关额外依赖
    settings.yaml 静态 headers ❌ 所有会话共用一个 id → 缓存落空、变慢变贵
    全局 opencode_zen profile(第三方 custom-header 插件) ❌ 对所有主机改写 UA + x-opencode-* 第三方插件 + 客户端 bundle
    dsh-opencode-session-header 插件 ✅ 直接用 DSH 真实会话 id ✅ 仅 opencode.ai ✅ 改 JSON 立即生效

    结论:静态 header 治标不治本(缓存经济学上亏);全局改写会污染其他 provider(包括自建网关的抓包排查);按会话、按域名白名单注入的专用插件是正确解。

    3. 解决方案:按会话注入 x-opencode-session

    3.1 插件机制

    dsh-opencode-session-header(npm: dsh-opencode-session-header,零依赖)在 fetch 传输层做手脚,不触碰任何模型适配器代码:

    llm/stream 流式迭代
    │ 每个流迭代包在 AsyncLocalStorage 里,携带 GenerateOptions.sessionId
    │ (稳定:同一会话/分支/子代理共用;换会话/分支/fork 则新 id)

    fetch middleware 链(vendored 自 pi-fetch-pipeline, MIT,用独立 Symbol 命名空间)
    │ host 命中白名单(默认 opencode.ai + 子域)→ 盖上 x-opencode-session
    │ 其他 host → 字节级原样放行

    底层 globalThis.fetch

    三个关键设计:

    • 会话语义正确:直接用 DSH 的会话 id(fresh per conversation / fork / subagent),保证 prompt-cache 命中;
    • 作用域严格:默认只对 opencode.ai 生效,其他 provider、内网网关、抓包代理流量完全不变;
    • 运行时开关:状态文件 <DSH_HOME>/plugins/dsh-opencode-session-header.json,{"enabled": false} 即关,每次 LLM 请求前重读、无需重启;文件缺失 = 启用。兜底 id 为 dsh-default。

    3.2 安装(以 CLI 环境为例)

    dsh plugin –profile web add dsh-opencode-session-header
    # 或显式指定版本 / 本地目录
    dsh plugin –profile web install "file:/path/to/dsh-opencode-session-header"

    装完手动重启一次 dsh web(bundle 层启动时合成),启动日志应出现加载行;之后正常发请求即可。

    3.3 真实排障案例:装上了,但 Desktop 就是加载不到

    这是本博客最有踩坑价值的一段。现象:~/.dsh/profiles/web/node_modules/dsh-opencode-session-header 明明存在,DSH Desktop 却始终不加载。

    根因:两套完全独立的 DSH 环境。

    CLI(终端 dsh)DSH Desktop(Electron App)
    dsh 本体 /usr/local/lib/node_modules/@deepseek-ai/dsh(v0.1.2-rc.1) /Applications/DSH Desktop.app(内置 v0.1.2-alpha.1)
    DSH_HOME ~/.dsh ~/Library/Application Support/dsh-desktop/harness
    web profile ~/.dsh/profiles/web <DSH_HOME>/profiles/web

    证据链(Desktop 自身日志 ~/Library/Logs/DSH Desktop/harness.log):

    [stdout] [harness-node] DSH_HOME=/Users/hello/Library/Application Support/dsh-desktop/harness
    [stdout] [harness-node] loading=/Applications/DSH Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/dsh/lib/bin.js

    而 Desktop 的 profiles/web/package.json 里:dependencies 没有该插件、dsh.profile.bundles 没有、cordis.patch.yml 没有 insert、node_modules grep 为空——插件从未被装进 Desktop 的 profile。它不是"加载失败",是"无物可载"。CLI 的 ~/.dsh 对它来说形同虚设。

    修复要点:必须用 Desktop 自己的工具链装进 Desktop 自己的 DSH_HOME。

    export DSH_HOME="$HOME/Library/Application Support/dsh-desktop/harness"
    export PATH="$DSH_HOME/.desktop-bin:$PATH" # Desktop 的 node/pnpm shim
    "$DSH_HOME/.desktop-bin/node" \\
    "/Applications/DSH Desktop.app/Contents/Resources/app/node_modules/@deepseek-ai/dsh/lib/bin.js" \\
    plugin –profile web install dsh-opencode-session-header

    plugin install 会在 pnpm 成功后自动把声明了 dsh.bundle.patch 的包追加进 dsh.profile.bundles,于是它成为真正的 profile 层。

    过程中踩到的三个次生坑:

  • pnpm 版本参数不兼容:pnpm 10 不认 –no-frozen-lockfile(“Unknown option: ‘frozen-lockfile’”),去掉该参数即可,pnpm 默认就会更新 lockfile;
  • 文件属主:用 root 跑安装,新文件是 root 属主,而 Desktop 以普通用户运行会读不动——需要把 profile 目录及 pnpm store 里新增文件 chown 回用户;
  • 必须重启才生效:patchReload: live 只热更新用户 patch 层,bundle 层只在启动时合成,重启后插件才进 Loader。
  • 验证三连(避免"以为好了"):

    # 1) 组合配置树里出现该 entry:
    dsh web –dump-config # → # == dsh-opencode-session-header
    # – id: dsh-opencode-session-header
    # name: dsh-opencode-session-header
    # 2) 启动日志无报错:grep -E "failed to load|entry did not activate|Failed to load plugins"
    # 3) 模块冒烟:desktop node -e "import('dsh-opencode-session-header')…" 打印 exports 齐全

    4. 场景分类(决策速查)

    同一个问题在不同形态下的处置差异很大,归纳为四维:

    维度 A:按报错形态

    场景症状处置
    A1 显式 400 所有 opencode-go 模型请求返回 MissingSessionID 装注入插件;确认 hosts 白名单含 opencode.ai、开关文件未禁用
    A2 隐式劣化 请求成功,但 id 每次/每会话变化或全局共用 无报错也要查:对照官方 Validated Clients 表,确保自己的客户端路径发送稳定会话 id;观察缓存命中率与账单
    A3 部分路径失败 有的模型路径带 session、有的不带(DSH 当前状态) 这正是 upstream Discussion #5495 跟踪的问题;发布前用插件在传输层兜底

    维度 B:按运行载体(DSH_HOME 决定一切)

    载体DSH_HOME安装方式坑
    纯 CLI ~/.dsh dsh plugin –profile web add <pkg>
    DSH Desktop ~/Library/Application Support/dsh-desktop/harness 必须用 Desktop 内置 shim + bin.js + 指向其 DSH_HOME;或 App 内插件入口/市场 最容易中招:把 ~/.dsh 当共享目录
    打包可执行 / 多用户机器 随启动参数 注意模块后备机制差异(普通 Node 用 symlink,pkg/Electron 用 ESM 代理,见 .dsh-module-fallback);关注文件属主而非只看"文件存在"

    维度 C:按安装来源

    来源命令/入口注意
    npm registry plugin … add <name> 版本未锁定则写 ^x.y.z;官方提示暂用 install –no-frozen-lockfile 与 pnpm 10 不兼容
    本地/仓库目录 plugin … install "file:/abs/path" 或相对路径 pnpm 以 profile 目录为 cwd,相对路径会按调用目录锚定(anchorPathSpec)
    插件市场(dshmarket) App 内入口 Desktop 场景注意 allowRestart: false 补丁;商业场景慎用自动重启

    维度 D:按治理/安全边界

    关注点推荐做法
    header 作用域 只对白名单域名注入(默认 opencode.ai),避免把 x-opencode-session/UA 改写到自建网关或其他 provider
    会话语义 每会话稳定、fork/子代理刷新新 id;不要用全局固定值(缓存失效 + 路由不优)
    灰度开关 用运行时开关 JSON,改文件即生效、免重启,故障时可秒级关闭
    多环境漂移 CLI 与 Desktop 各装各的;用 –dump-config、启动日志、模块冒烟做三方验证,而不是"目录里看到了文件"

    5. 复盘:三条经验

  • “装上了” ≠ “装对了地方”。DSH 有多个平级、互不共享的数据目录(尤其 CLI 与 Desktop 各带一套 Harness 与版本)。排查插件不生效,第一步永远是确认它装进了目标进程真正使用的 DSH_HOME/profile。
  • 用自己的工具链修自己的环境。跨版本拷贝 node_modules、混用 pnpm 版本(v10 vs v12)都会引入新故障;正确姿势是"目标环境的 shim + 目标环境的 CLI + 目标环境的 HOME"。
  • 验证要有"三层证据":配置文件(–dump-config 组合树)→ 运行日志(启动段无 failed to load)→ 运行时行为(模块可 import、header 实际出现)。缺任何一层都可能在"看起来好了"上翻车。

  • 参考链接

    • OpenCode Go 官方文档:https://opencode.ai/docs/go/(会话要求见 #where-can-i-use-it)
    • 上游跟踪:DeepSeek Harness Discussion #5495
    • 插件:dsh-opencode-session-header(MIT,零依赖)
    赞(0)
    未经允许不得转载:171主机测评 » AI | dsh报错失败400: {“type“:“MissingSessionID“,“message“:“Error from provider (Console Go)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址