Repository navigation
Replies: 2 comments
Startup-failure cluster: one symptom, three distinct root causesThere are 100+ discussions filed with the same symptom — the desktop app refuses to start and shows Where the log is
Look at the
Two of the reports already noticed that the port changes from launch to launch (#11192000, #11624, #11410, #12080) and folded it into the cookie story. That observation is correct but it is a second mechanism: with a forbidden port the failure happens before the handshake, so no 431 is ever logged — and the port is what decides whether you get that error, not the cookie count. If your log shows Full symptom list for cross-referencing / duplicate closing: If your crash log shows none of the three signatures above, paste the |
Census, and one adjacent thread worth fixing together
For anyone arriving here from one of the 431 threads: your fix is to clear the accumulated |
Uh oh!
There was an error while loading. Please reload this page.
Summary
On startup the desktop shell spawns the embedded host with
--port 0, so the OS picks the port. On machines whose dynamic port range reaches into the low IANA range, the OS can assign a port that is on the WHATWG Fetch forbidden port list (badPortsin undici). The shell then performs its one-time cookie handshake withfetch()on that URL, and undici rejects it locally — no packet is ever sent:hostCookie = await authenticateWebHost(ready.url)throws, the backend controller treats it as fatal, and the user gets the "DeepSeek Harness is unavailable" dialog. That dialog's third button is Disable third-party plugins, so the incident reads as "a plugin broke my install" — the plugins are unrelated.Every restart draws a new random port, so it almost always recovers. That is why it is rare and easy to dismiss — but it is a real startup-fatal path, and it costs the user a scare plus a support round each time it fires.
Environment
@deepseek-ai/dsh-desktop0.2.1-rc.5 (dshBuildCommit d4db512a969fccf0c5cc7ec8fe38e9c1d27af916)--port 0Observed
%APPDATA%\@deepseek-ai\dsh-desktop\logs\crash-2026-11-04T04-05-18-460Z-host.log:renderer console: no error-level output captured.Root cause chain
dsh/node_modules/@deepseek-ai/dsh-desktop-host/lib/index.js:238— the host is spawned with"--port", "0", i.e. any port the OS hands out..../dsh-desktop-host/lib/index.js:342— the ready URL is built ashttp://127.0.0.1:${ctx.webServer.port}.dsh-host-webserver/lib/index.js:293returnsthis.listenedPort, assigned at:473fromthis.server.address().port, so the port really is bound and the host has no reason to distrust it.resources/app/lib/main.js:8289—authenticateWebHost(url)callsfetch(url, { redirect: "manual" });:12250—hostCookie = await authenticateWebHost(ready.url).dsh/node_modules/undici/lib/web/fetch/constants.js—badPorts(79 entries) holds the classic unsafe ports, including1719 1720 1723 2049 3659 4045 4190 5060 5061 6000 6566 6665-6669 6679 6697 10080;lib/web/fetch/index.js:588turns that intomakeNetworkError('bad port')before any socket work. Max value is10080;'0'is deliberately absent, so--port 0itself is fine — only the result can be bad.netsh int ipv4 set dynamicport tcp start=... num=...(ours:start=1024 num=13977=> 1024–15000) hand out low ports, and those do collide. Worth adding: even if the handshake were fixed, Chromium refuses to navigate to a bad port (ERR_UNSAFE_PORT), so this has to be prevented at bind time, not only in the fetch call.Measured overlap on the affected machine: 19 of 13977 ports => ~0.14 % per launch (about 1 in 735 starts), which matches how intermittent it looked.
Reproduction
Deterministic on Windows (admin shell):
then keep launching the desktop app until the assigned port falls in the overlap; it fails immediately with
bad port. To make it immediate, force the host onto a forbidden port (a wrapper passing--port 6000, or stubwebServer.portto6000) and start the app:authenticateWebHostthrows with exactly the trace above.Suggested fixes (any one is sufficient)
dsh-host-webserverimmediately after:473— one small block, and it also protects thedsh web-> browser handoff on hostile ranges.--port <n>, and retry a bounded number of times onEADDRINUSEindsh-desktop-host.resources/app/lib/main.js:8390-8440the fatal dialog only special-cases/\blisten EADDRINUSE\b/u(restart-only, plus the "another DSH instance is running" copy). Abad portfailure deserves the same treatment — offer Quit / Restart and say a retry normally succeeds — instead of pointing at plugins.Workaround applied here
Raising the ephemeral range above 10080 empties the intersection with
badPorts.Pointers
dsh/node_modules/@deepseek-ai/dsh-desktop-host/lib/index.js:238,342dsh/node_modules/@deepseek-ai/dsh-host-webserver/lib/index.js:293,473resources/app/lib/main.js:8289,12250,8390-8440(fatal dialog),:7345/:7512(startupFailedcopy, en/zh)dsh/node_modules/undici/lib/web/fetch/constants.js(badPorts),.../fetch/index.js:588All reactions