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
{{ message }}
Repository navigation
Webview becomes unreachable with HTTP 431 after several restarts: auth cookie name is derived from host:port, so cookies accumulate without bound
#8073
After the desktop app is restarted a number of times, all webview requests start failing with HTTP 431 (Request Header Fields Too Large) and the client gets stuck on the loading screen. Deleting the browser cookie store for the app immediately restores it — until the cookies accumulate again. We root-caused this: the auth cookie's name hashes the full request authority (host:port), while the embedded server's port changes on every launch, and browsers isolate cookies by domain, not by port. Each restart therefore mints a brand-new cookie name that can never be overwritten, and the Cookie header grows one entry per restart until it exceeds Node's default 16 KB header limit.
Affected code
packages/client/connection/src/browser-auth.ts (verified unchanged at tag dsh-v0.1.7-rc.2; the same logic ships in the npm builds of @deepseek-ai/dsh-client-connection 0.1.2-rc.1 → 0.1.5-rc.3):
// line 69: "Canonical request authority used as the cookie name and signed audience."// line 106:functioncookieName(authority: string): string{returnCOOKIE_PREFIX+encodeBase64Url(createHash('sha256').update(authority).digest())}
authority here is the full host:port (see requestAuthority(req.headers) at line 243), and the embedded server is started on an ephemeral port, so the derived cookie name changes on every launch.
Failure chain
The local server listens on an ephemeral port each run (--port 0 pattern in the desktop shell).
Cookie name = dsh-auth- + base64url(sha256(host:port)) → a new name every restart.
Browsers key the cookie jar by domain (RFC 6265 — cookies are not port-isolated), so old cookies are never overwritten, only appended.
Every request to the app origin carries the whole jar: N restarts → N dsh-auth-* cookies.
The Cookie request header crosses Node's default 16 KB maxHeaderSize → the webview's bundle/resource requests return 431.
The frontend treats non-200 as load failure → boot/plugin loading stalls with no visible error.
Evidence
Observed on DSH Desktop (macOS, embedded dsh 0.1.2-rc.1, later 0.1.5-rc.3) after daily restarts across multiple days:
With --remote-debugging-port attached, the failing request was the webview bundle with HTTP 431; other origins were fine.
The app's cookie store had grown to ~56 KB (dozens of dsh-auth-* entries, one per past port).
Deleting the cookie store restored boot immediately (jar back to ~20 KB); it re-accumulated at ~1 cookie per restart until the next 431.
Reproduce: launch the app repeatedly and watch one dsh-auth-<sha256(host:port)> cookie get added per distinct port in devtools → Application → Cookies, until requests start returning 431.
Suggested fix
Keep the cookie name stable by hashing the hostname only, and keep the token's signed audience bound to the full authority (unchanged): when the port changes, old tokens simply fail validation → 401 → the server re-issues a fresh cookie under the same name, so the jar self-overwrites and stays bounded. The existing launch-token flow (tokenMatches / launchToken) already provides exactly this re-issuance path, so the fix composes with the current auth design and does not weaken it.
This patch has been running in our local production builds since 2026-09-11 across 40+ restarts: no 431 recurrence, and token invalidation on port change behaves exactly as before (verified for bare-host / host:port / IPv6 authority forms). As defense-in-depth one could additionally prune expired dsh-auth-* cookies at boot or raise the server's maxHeaderSize, but the name fix alone closes the loop.
Happy to provide more details (CDP captures, cookie-store dumps) if useful.
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.
Summary
After the desktop app is restarted a number of times, all webview requests start failing with HTTP 431 (Request Header Fields Too Large) and the client gets stuck on the loading screen. Deleting the browser cookie store for the app immediately restores it — until the cookies accumulate again. We root-caused this: the auth cookie's name hashes the full request authority (
host:port), while the embedded server's port changes on every launch, and browsers isolate cookies by domain, not by port. Each restart therefore mints a brand-new cookie name that can never be overwritten, and theCookieheader grows one entry per restart until it exceeds Node's default 16 KB header limit.Affected code
packages/client/connection/src/browser-auth.ts(verified unchanged at tagdsh-v0.1.7-rc.2; the same logic ships in the npm builds of@deepseek-ai/dsh-client-connection0.1.2-rc.1 → 0.1.5-rc.3):authorityhere is the fullhost:port(seerequestAuthority(req.headers)at line 243), and the embedded server is started on an ephemeral port, so the derived cookie name changes on every launch.Failure chain
--port 0pattern in the desktop shell).dsh-auth-+ base64url(sha256(host:port)) → a new name every restart.dsh-auth-*cookies.Cookierequest header crosses Node's default 16 KBmaxHeaderSize→ the webview's bundle/resource requests return 431.Evidence
Observed on DSH Desktop (macOS, embedded dsh 0.1.2-rc.1, later 0.1.5-rc.3) after daily restarts across multiple days:
--remote-debugging-portattached, the failing request was the webview bundle with HTTP 431; other origins were fine.dsh-auth-*entries, one per past port).Reproduce: launch the app repeatedly and watch one
dsh-auth-<sha256(host:port)>cookie get added per distinct port in devtools → Application → Cookies, until requests start returning 431.Suggested fix
Keep the cookie name stable by hashing the hostname only, and keep the token's signed audience bound to the full authority (unchanged): when the port changes, old tokens simply fail validation → 401 → the server re-issues a fresh cookie under the same name, so the jar self-overwrites and stays bounded. The existing launch-token flow (
tokenMatches/launchToken) already provides exactly this re-issuance path, so the fix composes with the current auth design and does not weaken it.This patch has been running in our local production builds since 2026-09-11 across 40+ restarts: no 431 recurrence, and token invalidation on port change behaves exactly as before (verified for bare-host / host:port / IPv6 authority forms). As defense-in-depth one could additionally prune expired
dsh-auth-*cookies at boot or raise the server'smaxHeaderSize, but the name fix alone closes the loop.Happy to provide more details (CDP captures, cookie-store dumps) if useful.
中文附言(可选,加在帖尾)
相同问题:桌面版每次重启端口变化,而鉴权 cookie 名对
host:port整体做哈希、浏览器 cookie 又按域不按端口隔离——于是旧 cookie 永不被覆盖,累积到 Cookie 头超过 16KB 后 webview 全部 431 卡死;清 cookie 立即恢复。建议 cookie 名只哈希 hostname,令牌签名仍绑完整 authority(端口变化→旧令牌 401→同名重发),已在本机 16 天生产验证。补丁与证据如英文正文。All reactions