You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Error: desktop welcome: Web authentication failed
at connectDesktopWelcome (file:///D:/DeepSeek%20Harness/resources/app.asar/lib/main.js:8886:31)
at async Object.start (file:///D:/DeepSeek%20Harness/resources/app.asar/lib/main.js:12195:22)
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
1. 问题现象
安装第三方插件后重启 DeepSeek Harness,弹出启动失败对话框,应用无法进入工作区:
崩溃日志位于
%APPDATA%\@deepseek-ai\dsh-desktop\logs\crash-<timestamp>-host.log:触发条件与插件无关。本例中插件安装只是让重启次数多了一次,恰好越过了临界点。
2. 启动认证链路
主进程(
lib/main.js)的启动顺序:host.start()拉起本地 Host 子进程(dsh-desktop-host),监听127.0.0.1:<随机端口>ready.url形如:http://127.0.0.1:64932/?token=<launchToken>authenticateWebHost(ready.url)—— 用 Node 原生 fetch 请求该 URL,期望303 + Set-CookieconnectDesktopWelcome(ready.url, net.fetch)—— 用 Electron net.fetch(Chromium 网络栈) 再访问同一 URL,跟随 303 跳到/,靠 Chromium 里已保存的 cookie 完成认证关键:只要响应不是 2xx 就抛这个错,且错误信息不携带状态码,导致日志里看不到真正原因。
注意第 3 步用 Node fetch(无 cookie jar,一定成功),第 4 步用 Chromium(携带全部 cookie,才是失败的那一步)。
3. 根因
3.1 cookie 命名方式(源码)
@deepseek-ai/dsh-client-connection的browser-auth.js:authority是规范化后的hostname:port,即127.0.0.1:64932。签发逻辑(
authorizeIndex):sessionCookie的固定属性:cookieMaxAgeDays默认 30 天。文档注释明确说明是刻意把 hostname + port 同时绑进「确定性名称」和「签名 payload」。3.2 为什么会累积
桌面端用
--port 0启动 Host,每次启动端口都不同 →authority不同 → cookie 名的 sha256 不同 → 每次都是一个全新的 cookie 名。而 HTTP 的
Set-Cookie只能新增,或者覆盖「同名 + 同 Path + 同 Domain」的那一条。名字不同,新的 303 响应覆盖不到任何一条旧 cookie,也没有任何删除逻辑:authorizeIndexset-cookie,无清理代码isAuthenticatedcookieValue(cookieHeader, 当前 authority 的名字)精确匹配,只读当前那条所以那 70 条旧 cookie 对服务端毫无用处,唯一作用是撑大请求头。
3.3 为什么最终是 431
Host 的 HTTP 服务使用 Node 默认
maxHeaderSize = 16384字节。实测单条 cookie:累积到约 70 条即触顶。
3.4 为什么不会自愈
Chromium 单域名 cookie 上限是 180 条,超出才驱逐最旧的。而服务端在 约 70 条时就已经因为 16KB 请求头拒绝请求。于是浏览器永远等不到自动清理那一步——必然先撞 431。
4. 实测数据
手动以 host 模式启动运行时(不经过 Electron 外壳),用真实 cookie 值构造请求头:
逐个叠加 cookie 条数测试
GET /:200 OK200 OK200 OK200 OK(临界内)431 Request Header Fields Too Large其他实测结论:
GET /无有效 cookie →401 dsh web authentication requiredGET /带有效 cookie →200GET /?token=<launchToken>→303+set-cookie(同一 token 可重复使用,不是一次性)现场数据吻合
dsh-auth-*共 71 条,全部host_key='127.0.0.1',最早 10-18,最后一条写入时间 01:09:02 UTC5. 影响
70 ÷ 每日启动次数天后必然发作(本例 9 天积累 71 条)dshCLI 模式几乎不受影响——这是该缺陷只在桌面端爆出的原因6. 临时处置(用户侧)
前提:完全退出应用(Chromium 运行时会独占 cookie 库,无法写入)。
%APPDATA%\@deepseek-ai\dsh-desktop\Network\Cookieshost_key = '127.0.0.1'的记录注意:
$DSH_HOME/.credentials.yaml),清掉不影响登录7. 建议的正式修复
方案 A(推荐):cookie 名固定,authority 移入签名 payload
isAuthenticated继续校验payload.authority === 当前 authorityMax-Age=0做清理方案 B:改为会话 cookie
去掉
Max-Age/Expires,随 Chromium 关闭失效。可与其他方案叠加,作为兜底。方案 C:签发时主动回收
服务端在 303 响应中遍历请求头
Cookie里所有dsh-auth-*名字,对≠ 当前 authority的逐个下发删除(一个响应可携带多条Set-Cookie):可单独使用,也可作为兼容层,配合方案 A 使用。
方案 D(附带):改进错误可观测性
connectDesktopWelcome抛错时携带response.status与response.headers.get('content-type'),例如:当前实现只判断
!authenticated.ok,把 431/401/500 全部压成同一句话,是这次排查成本高的主要原因。8. 复现步骤(供提 issue 使用)
Network/Cookies注入 71 条host_key='127.0.0.1'的dsh-auth-*记录)Network/Cookies中dsh-auth-*条数随启动次数线性增长,且都是不同名字desktop welcome: Web authentication failed(lib/main.js:8886)127.0.0.1的 cookie 后立即恢复9. 一句话结论
cookieName()把 hostname+port 的 sha256 拼进 cookie 名,而桌面端每次用随机端口启动、cookie 有效期 30 天、且无任何回收逻辑——三者叠加使每个「端口-进程」组合永久残留一条约 226 字节的 cookie,约 70 条后击穿 Node 的 16KB 请求头上限,Host 返回 431,主进程将其报为Web authentication failed。属于设计层面的组合缺陷(不是用户操作问题,也与第三方插件无关)。修复成本极低:把 authority 从 cookie 名移入签名 payload 即可,安全性不变。
All reactions