WorkBuddy 对 DSH 下手了:400 报错(Illegal API invocation from an unapproved channel)的真相与解法
一句话结论:这个报错跟「渠道」一点关系都没有。上游在对 system prompt 做关键词内容风控,命中
DeepSeek Harness就拒。解法是在两个词之间插一个看不见的零宽空格 —— 语义不变、上下文不丢、完全无损。
那天晚上发生了什么
如果你也在用 DeepSeek Harness(DSH)通过 WorkBuddy 渠道跑模型,某天晚上大概会突然看到这个:
buddy: 请求被拦截,请重新发送
Illegal API invocation from an unapproved channelHTTP 400,上游 code 11128(有时是 11140)。
不是偶发,是全部请求都挂。换账号、重启、重装插件,全都没用。
社区里当时的猜测基本都指向「渠道被封了」「账号被风控了」—— 因为报错文案本身就是这么写的:*unapproved channel*,未获批准的渠道。
但这个文案在骗你。
60 秒自证:这不是渠道问题
不用信我,你自己两分钟就能验证。
用同一个 token,打两次请求,只改 system prompt 里的一个词:
| system prompt 内容 | 结果 |
|---|---|
You are an AI agent powered by DeepSeek Harness. | ❌ HTTP 400 / 11128 |
You are an AI agent. | ✅ HTTP 200 |
如果符合这个规律,答案就已经出来了 —— 跟渠道、账号、客户端身份、请求头,都没有关系。
⚠️ 一个容易误判的细节
关键词要配合请求体足够大(大约 >1KB)才会稳定触发。
所以短 prompt 即使含关键词也会被放过 —— 这是最容易让人误判成「偶发故障」「网络抽风」的地方。用最小请求去测,会测不出来。
我已经排除过的假设(别重复踩坑)
这些都是我逐项实测排除的,列出来省你的时间:
| 假设 | 实测结果 |
|---|---|
| 账号被风控 | 换账号同样报错 |
域名不对(copilot.tencent.com vs www.workbuddy.ai) | 国内站国际站都试过,都失败 |
| 客户端身份不对(WorkBuddy vs CodeBuddy) | 两种都试过 |
| 缺请求头(13 个都补齐) | 仍然失败 |
reasoning_effort 值非法 | 单独测也返回 200 |
max_tokens 超限 | 调小也一样 |
| 工具数量太多(316 个) | 减到少量仍然失败 |
| 网络 / 代理 | 直连也一样 |
| 并发过高 | 12 并发全成功过 |
全都不是原因。
真因:关键词内容风控
上游在请求发出前,会对 messages 做一次关键词匹配。
命中的规则,是 system prompt 里的这一段身份说明:
You are an AI agent powered by DeepSeek Harness.只要出现 DeepSeek Harness 这个词组,请求直接被拒 —— 报的还是那句误导性的 "unapproved channel"。
至于为什么要拦这个词,我不做推测。对使用者来说,重要的是怎么在不损失功能的前提下绕过去。
解法:插一个看不见的字符
在 DeepSeek 和 Harness 之间,插入一个零宽空格 U+200B:
// 改前
"You are an AI agent powered by DeepSeek Harness."
// 改后(中间那个字符是 U+200B,肉眼看不见)
"You are an AI agent powered by DeepSeek\u200B Harness."就这么简单。为什么有效:
- 模型看到的语义完全不变 —— 零宽空格不参与分词,模型读到的是同一个意思
- 上游的子串匹配不再命中 ——
DeepSeek Harness这个连续字符串不存在了 - 无损 —— 没有裁剪、没有改写、没有丢上下文、没有降级
实测数据
用真实的 DSH 请求形状测(316 个 tools + effort=max + max_tokens=128000):
原文 → ❌ 0/5 全部失败
中和后 → ✅ 5/5 全部成功⚠️ 一个必须注意的坑:路径不能被改写
这个坑我踩过,会导致工具调用全部失败。
DSH 的运行时上下文里包含真实文件路径,比如:
D:\10_COD\DeepSeek Harness\resources\app.asar\dsh\如果你无脑全局替换,路径会变成:
D:\10_COD\DeepSeek Harness\resources\... ← 中间插了零宽字符模型会把这个坏路径传给工具,然后一切文件操作都失败。而且因为零宽字符肉眼不可见,你会盯着屏幕看不出任何异样,只会觉得「文件怎么突然不存在了」。
正确做法是给匹配器加前后负向断言,跳过路径语境:
/(?<![\\/])(DeepSeek)[ \u00A0]+(Harness)(?![\\/])/gi(?<![\\/]) 表示前面不能是斜杠,(?![\\/]) 表示后面不能是斜杠 —— 这样路径里的 DeepSeek Harness 就会被完整保留。
把它做成了插件
手工改 prompt 不现实(每次都要改、还容易漏)。所以我把它写成了 DSH 插件,叫 wb400修复。
装在 DSH 的「设置」页里,4 个开关:
| 开关 | 默认 | 作用 |
|---|---|---|
| 总开关 | 开 | 关掉就完全恢复原始请求,一个字节都不改 |
| 中和身份关键词 | 开 | 上面说的零宽空格替换 |
| 补首条 system | 开 | 首条消息不是 system 时自动补一条(11128 的另一个成因) |
| 隐藏品牌词 | 关 | 更激进:连 DeepSeek 也打散 |
改完即时生效,不用重启。
它对你的代码做了什么
在 pack.js 里插入一行:
;globalThis.__wbLexicon?.rewrite?.(messages);位置在 workbuddy 的 messages 组装完成、发出去之前。
- 只影响 WorkBuddy / CodeBuddy 两个 provider 的出站请求
- 钩子未安装时可选链短路,行为与未打补丁完全一致
pnpm重装会冲掉这行 —— 插件启动时自动自愈重打
快速安装
前置条件:
| 项 | 要求 |
|---|---|
| DSH 桌面版 | 已在用 |
| dsh-our-free-model | 已安装并登录至少一个 WorkBuddy / CodeBuddy 账号 |
| Node.js | ^22.19.0 或 >=24.0.0 |
不需要:官方客户端后台常驻、额外 API Key、管理员权限。
# 或者直接在 DSH 里说「把 <目录绝对路径> 装成插件」装完重启一次 DSH,在「设置」里就能看到 wb400修复。
仓库:https://github.com/balongTTY/dsh-workbuddy-fix
另一个相关插件
如果你还想知道每次请求烧了多少积分,配套的计量面板在:
https://github.com/balongTTY/dsh-workbuddy-meter
两个插件可以同时装,而且开关是联动的(共享同一个状态文件 ~/.dsh/workbuddy-meter/lexicon.json)—— 在任一边改开关,另一边立即跟随。
也可以只装一个:
- 只装 fix → 有修复功能,没有计量面板
- 只装 meter → 两者都有(meter 内置了同样功能)
- 都装 → 修复逻辑一致,开关双向同步
错误码速查
顺手做个对照表,方便搜到的人直接定位:
| 上游 code | 报错文案 | 真实原因 | 这个插件能修吗 |
|---|---|---|---|
| 11128 | Illegal API invocation from an unapproved channel | 上游按关键词拦 system prompt | ✅ 能 |
| 11128 | first message is not system prompt | 首条不是 system 角色 | ✅ 能(补首条 system 开关) |
| 11140 | 安全策略拦截 | 同一错误族 | ✅ 能 |
| 11133 | integer_below_min_value | max_tokens 太小 | ❌ 调大 max_tokens |
| 6004 | usage exceeds frequency limit | 上游限流 | ❌ 换账号或等冷却 |
| —— | EMPTY_RESPONSE(有思考无正文) | 推理模型把 max_tokens 吃光 | ❌ max_tokens 设 ≥4096 |
已知边界(诚实说明)
- 只对本机 OFM 进程内的请求生效;服务器上做反代的流量要单独装
- 上游如果换了关键词(不再拦
DeepSeek Harness),这个插件就不需要了 —— 关掉总开关即可 - 钩子插入点依赖 OFM 的内部结构;OFM 大版本升级后若锚点消失,面板会显示「未打钩子」并给出原因
- 本文不推测上游为什么要拦这个词,只解决「怎么不受影响」
小结
| 报错 | Illegal API invocation from an unapproved channel / code 11128 |
|---|---|
| 文案说 | 渠道未获批准 |
| 真相 | 上游对 system prompt 做关键词风控,命中 DeepSeek Harness |
| 解法 | 插入零宽空格 U+200B,语义不变 |
| 注意 | 文件路径不能被改写,匹配器要跳过路径语境 |
| 工具 | wb400修复 |
如果你的现象跟上面不一样(比如短 prompt 也报错、或者换了模型就好),欢迎到仓库开 issue,把请求贴出来一起看。
后来
这个工具修好之后,我在改它的过程中把宿主搞崩了四次 —— 同一个 bug 的两个副本,第三次是我第一次「修好」之后两天又崩的。
那件事的复盘在另一篇:我写的 DSH 插件把宿主搞崩了四次,最后发现是同一个 bug 的两个副本
如果你也写包装 globalThis.fetch 的插件,那篇可能比这篇更有用 —— 里面有 RangeError: Maximum call stack size exceeded 的完整根因链,以及「测试全绿但功能坏了」是怎么发生的。