DeepSeek Harness desktop update stop-failed: manual install
The most misleading thing about "cannot safely stop tasks" is that it sounds like "you still have a task running", so people close sessions, stop agents, and wait — then discover that fully quitting the app with no session open still produces the same error when they click update. Unpacked, the message says exactly one thing: the host process must be stopped before installing, and the host did not exit within 10 seconds; the update package was downloaded and verified long ago (#11187, #11361).
DeepSeek Harness: Layer 1: it is about "stopping", not "tasks"
The message corresponds to the stop-failed branch: before updating, the app locks task state for a snapshot, then calls backend.stop() to stop the host; when the host ends with an unclean exit, this error is thrown.
- In the update flow in
apps/desktop/src/main.ts, it first callshost.updateTasks('lock')to capture task state. - It then calls
backend.stop()to stop the host. - If the host exits uncleanly during the stop,
updateStopFailureis set andstop-failedis thrown. - What the UI shows is this sentence (Chinese at
locale.ts:316, English at:137). - Three sibling failures have their own wording:
tasks-changed,tasks-unavailable, andfullscreen-timeout. The sentence you see maps only tostop-failed, so do not treat it as the other three.
| Branch | Trigger | Direction |
|---|---|---|
stop-failed | host did not exit within 10 seconds | Fully quit, then install manually |
tasks-changed | task state changed during the update | Wait for tasks to finish, then update |
tasks-unavailable | task snapshot unavailable | Restart and try again |
fullscreen-timeout | fullscreen exit timed out | Leave fullscreen and retry |
DSH plugin: Layer 2: "exit 0 / signal null" means it did exit cleanly
exit 0 and signal null in the technical details mean the host exited normally and was not force-killed; shutdown acknowledged true means it received the exit instruction and confirmed it.
- Click "view technical details" in the dialog and write down the whole line.
- Map the fields:
shutdown acknowledged: truemeans the exit instruction arrived;exit: 0 / signal: nullmeans a normal exit not killed by SIGTERM/SIGKILL. - So the word "unclean" has nothing to do with the process's final state, only with how long it took.
- It also means the package is fine: the download completed, the sha512 matches the remote
nightly-mac.yml, and no sudo is required.
DeepSeek Harness: Layer 3: "graceful deadline exceeded true" just means 10 seconds passed
DesktopHost.stop hardcodes a 10-second grace period (1e4); going past it marks the exit unclean, and even a successful SIGTERM/SIGKILL exit does not reverse that verdict.
- Look at the condition:
requireGraceful && (!graceful || exitCode !== 0 || !shutdownCompleted)throws if any part holds. gracefulcomes fromexitsWithin(exited, 1e4), in other words "exited within 10 seconds".- After the timeout it sends SIGTERM (wait 5 seconds) and then SIGKILL (wait 5 seconds); all three stages are cleanup only and do not change the verdict.
- The result: the host literally exits 0 and is still judged unclean, and the update is rolled back to the pending directory.
- That grace period is clearly too short for slow machines and plugin-heavy setups; the community suggests 30 seconds or making it configurable.
DSH Plugin: Root cause: the host never calls process.exit() after shutdown
After receiving shutdown, the host runs application.shutdown.shutdown(0), reports shutdown-complete, and calls process.disconnect(), but never process.exit(); finishing cleanup is not the same as ending the process.
- Look at
stop()indsh-desktop-host/lib/index.js: it ends after those three steps, with no exit. - Look at
createProcessShutdown()inprofile-boot: afterdispose()finishes it only callscomplete(code), which just setsprocess.exitCode, and then expects the event loop to drain on its own. - One referenced handle — a timer, socket, watcher, FFI callback channel — is enough to keep the event loop non-empty forever.
- The community isolated it: base + web-app alone exited in 131 ms; adding a plugin whose
apply()only containssetInterval(() => {}, 1000)kept it alive for 180 seconds. - So this is not koffi's fault; koffi is simply the handle that always exists on Windows.
The same defect also affects ordinary quits: the shell hides the window first and then waits on DesktopHost.stop(), so the process stays alive through 10 + 5 + 5 seconds, and the user sees "the window is gone but the process is still there".
DeepSeek Harness: Version check: fixed on master, not backported to rc
Master's 6252a29a (exit after acknowledged teardown, 2026-10-18, with test 23fb3ea) adds process.exit() at the end of the shutdown path, but it is not an ancestor of the rc.3 tag.
- Check the tag:
dsh-v0.2.1-rc.3points at1f81c047. - Verify containment:
git merge-base --is-ancestor 6252a29a 1f81c047returns false, so the release package cannot contain the fix. - In the packaged artifact
dsh/node_modules/@deepseek-ai/dsh-desktop-host/lib/index.js, there is indeed no exit after disconnect. - So everyone upgrading from rc.2/rc.3 hits this; it is not an isolated report.
- On their own machine the community added the same line and measured shutdown requested → shutdown-complete → exit in 56 ms, with the process vanishing immediately after Cmd+Q and no black screen.
One easy-to-miss corollary: the grace period affects only whether the stop is judged successful, not whether cleanup finishes. The whole sequence of waiting 10 seconds, signaling, and force-killing still runs — the conclusion was just written down as failure.
DSH plugin: Manual install: three executable paths
Make the host really disappear first, then run the already-downloaded pending package directly; the installer does not re-download and session data is unaffected.
- On Windows, confirm the process is really gone: the window's × only hides it to the tray, so end every
DeepSeek Harnessprocess in Task Manager. - Locate the pending package: the installer exe under
%LOCALAPPDATA%\@deepseek-ai\dsh-desktop-updater\pending\. - Run it directly; it restarts normally when finished, and session records under
~/.dsh/sessionsare not lost. - On macOS it is the same idea:
osascript -e 'quit app "DeepSeek Harness"', wait a few seconds, unpack the zip under~/Library/Caches/@deepseek-ai/dsh-desktop-updater/pending/into/Applicationswithditto -x -k, then runxattr -dr com.apple.quarantine. - Verify after installing:
codesign --verify --deep --strictpasses and the version number changed. - To reduce leftover handles, stop all plugins before updating so the host has a chance to exit within 10 seconds.
Cautions
Do not retry a stop check that is bound to time out.
- Do not keep clicking retry. Each attempt reruns a stop check that is bound to time out.
- Do not assume closing the window quits the app. The tray is the default behavior, the process is still there, and so is the host.
- Do not lump the sibling failures together. Only
stop-failedis the stop check; the other three have their own causes. - Read the technical details.
DesktopHostUncleanExitError's message only goes to the log; the dialog swallows it, and it is the only clue for the next step. - A manual install loses no data. Sessions and profiles live under
~/.dsh; the installer only replaces the application itself. - Remember this check also applies to ordinary quits. Tens of seconds of black screen after Cmd+Q is the same cause, not a freeze.
If your technical details show a non-zero exit or a signal of SIGKILL, the process really did not exit cleanly — check whether a plugin is stuck in cleanup.
If exit is 0, signal is null, and only graceful deadline exceeded true is set, this article matches exactly: go straight to the manual install.See DeepSeek Harness desktop: no window, crash, EMFILE, 80s for the related case.
FAQ
No, the message does not mean a task is still running: it maps to the stop-failed branch, where the DeepSeek Harness updater locks task state, stops the host, and throws this error when the host exits uncleanly. It says the host did not stop cleanly, which has nothing to do with whether a task finished.
DeepSeek Harness calls the exit unclean because the condition also includes graceful deadline exceeded true. DesktopHost.stop allows only a 10-second grace period, so a host that does not exit within 10 seconds of receiving shutdown is judged unclean even if it eventually ends with exit code 0.
Quitting the app fully does not help because the problem is not how cleanly you quit, but that the DeepSeek Harness host never calls process.exit() after receiving the exit instruction. Any referenced handle — a timer, socket, or watcher — keeps the event loop from draining, so the 10 seconds always run out.
The fixing commit in DeepSeek Harness is master's 6252a29a (exit after acknowledged teardown), but per the community check the rc.3 tag points at 1f81c047, which is not a descendant, so neither the rc.2 nor rc.3 release packages include the fix.
To install the update package you already have, end every DSH process first in DeepSeek Harness (the window's × only hides it to the tray), then run the pending exe under %LOCALAPPDATA%\@deepseek-ai\dsh-desktop-updater\pending directly. The installer does not re-download, and session records in ~/.dsh/sessions are unaffected.
Related Terms
- stop-failed
- stop-failed is one failure branch of the DeepSeek Harness desktop updater, meaning the pre-install host stop check failed. It differs from the sibling failures tasks-changed, tasks-unavailable, and fullscreen-timeout, which share similar wording but mean different things.— https://github.com/deepseek-ai/deepseek-harness/discussions/11361
- DesktopHostUncleanExitError
- DesktopHostUncleanExitError is the error thrown when the desktop host exit check fails, carrying the exit code, signal, whether shutdown was acknowledged, and whether it timed out. Its message only reaches the log; the dialog swallows it.— https://github.com/deepseek-ai/deepseek-harness/discussions/11187
- graceful deadline
- The graceful deadline is the grace period the updater gives the host to exit, currently 10 seconds. After it expires, SIGTERM and then SIGKILL are sent, but the final verdict still depends on whether the host exited inside the grace period.— https://github.com/deepseek-ai/deepseek-harness/discussions/11187
- shutdown-complete
- shutdown-complete is the IPC message the desktop host sends the shell after finishing cleanup. It means cleanup actions are done, not that the process has exited; without a following process.exit(), the event loop can still hang.— https://github.com/deepseek-ai/deepseek-harness/discussions/11187
Sources
- #11187 — macOS desktop: the built-in updater refuses to install because the host takes longer than 10 seconds to exit gracefully· deepseek-ai (GitHub Discussions)
- #11032 — 0.2.1-rc.3 still refuses the in-app update after the host confirms teardown· deepseek-ai (GitHub Discussions)
- #11361 — dsh update fails (cannot safely stop tasks)· deepseek-ai (GitHub Discussions)
- deepseek-ai/deepseek-harness (source repository)· GitHub