DeepSeek Harness desktop update stop-failed: manual install

TroubleshootingPublished 2026-10-30Author: DeepSeek Plugin Market
DeepSeek HarnessDSHdesktopupdate failurestop-failedshutdownmacOS
DeepSeek Harness refuses an in-app update with stop-failed. The package is downloaded; the host only misses the 10-second exit deadline, so install it by hand.

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.

  1. In the update flow in apps/desktop/src/main.ts, it first calls host.updateTasks('lock') to capture task state.
  2. It then calls backend.stop() to stop the host.
  3. If the host exits uncleanly during the stop, updateStopFailure is set and stop-failed is thrown.
  4. What the UI shows is this sentence (Chinese at locale.ts:316, English at :137).
  5. Three sibling failures have their own wording: tasks-changed, tasks-unavailable, and fullscreen-timeout. The sentence you see maps only to stop-failed, so do not treat it as the other three.
BranchTriggerDirection
stop-failedhost did not exit within 10 secondsFully quit, then install manually
tasks-changedtask state changed during the updateWait for tasks to finish, then update
tasks-unavailabletask snapshot unavailableRestart and try again
fullscreen-timeoutfullscreen exit timed outLeave 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.

  1. Click "view technical details" in the dialog and write down the whole line.
  2. Map the fields: shutdown acknowledged: true means the exit instruction arrived; exit: 0 / signal: null means a normal exit not killed by SIGTERM/SIGKILL.
  3. So the word "unclean" has nothing to do with the process's final state, only with how long it took.
  4. 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.

  1. Look at the condition: requireGraceful && (!graceful || exitCode !== 0 || !shutdownCompleted) throws if any part holds.
  2. graceful comes from exitsWithin(exited, 1e4), in other words "exited within 10 seconds".
  3. 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.
  4. The result: the host literally exits 0 and is still judged unclean, and the update is rolled back to the pending directory.
  5. 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.

  1. Look at stop() in dsh-desktop-host/lib/index.js: it ends after those three steps, with no exit.
  2. Look at createProcessShutdown() in profile-boot: after dispose() finishes it only calls complete(code), which just sets process.exitCode, and then expects the event loop to drain on its own.
  3. One referenced handle — a timer, socket, watcher, FFI callback channel — is enough to keep the event loop non-empty forever.
  4. The community isolated it: base + web-app alone exited in 131 ms; adding a plugin whose apply() only contains setInterval(() => {}, 1000) kept it alive for 180 seconds.
  5. 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.

  1. Check the tag: dsh-v0.2.1-rc.3 points at 1f81c047.
  2. Verify containment: git merge-base --is-ancestor 6252a29a 1f81c047 returns false, so the release package cannot contain the fix.
  3. In the packaged artifact dsh/node_modules/@deepseek-ai/dsh-desktop-host/lib/index.js, there is indeed no exit after disconnect.
  4. So everyone upgrading from rc.2/rc.3 hits this; it is not an isolated report.
  5. 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.

  1. On Windows, confirm the process is really gone: the window's × only hides it to the tray, so end every DeepSeek Harness process in Task Manager.
  2. Locate the pending package: the installer exe under %LOCALAPPDATA%\@deepseek-ai\dsh-desktop-updater\pending\.
  3. Run it directly; it restarts normally when finished, and session records under ~/.dsh/sessions are not lost.
  4. 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 /Applications with ditto -x -k, then run xattr -dr com.apple.quarantine.
  5. Verify after installing: codesign --verify --deep --strict passes and the version number changed.
  6. 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.

  1. Do not keep clicking retry. Each attempt reruns a stop check that is bound to time out.
  2. 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.
  3. Do not lump the sibling failures together. Only stop-failed is the stop check; the other three have their own causes.
  4. 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.
  5. A manual install loses no data. Sessions and profiles live under ~/.dsh; the installer only replaces the application itself.
  6. 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

DeepSeek Harness shows cannot safely stop tasks and the update is not installed — is a task still running?

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.

Technical details say exit 0 and signal null, so why is the DeepSeek Harness exit called unclean?

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.

Why does fully quitting DeepSeek Harness and updating again still fail?

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.

Which DeepSeek Harness version fixed this update failure?

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.

Without waiting for a fix, how do I install the update package I already have for DeepSeek Harness?

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