Desktop auto-update failed: residual processes and recoverable state
Focus on the failure path: stop local services that hold files or ports before install, restore what was stopped on failure, and distinguish check, download and pending-restart states in the UI.
- Tauri
- Desktop updates
- Process lifecycle
- Failure recovery
Related case: AI Agent application platform. This note follows the control flow of a local Tauri update service. Examples omit business command names and endpoints and only express the order of stop, install, restart and fallback. It does not claim that any live incident was automatically recovered.
Update is not “download finished”
People often treat auto-update as check → download → install → restart. The fragile part is the local state in between:
- the app itself is still running while the installer wants to replace occupied files;
- local services started earlier (MCP, gateways, helpers) may hold ports or directories;
- if download or install fails, a single “update failed” message does not say whether the old version still works;
- on Windows, leftover processes can keep the next launch pointed at stale resources.
So narrow the question: on failure, does the system leave an explainable state the user can continue from?
Stop resource-holding processes before install
Before calling install, the local update service stops auxiliary services it started and records that they were stopped. The point is not cosmetic cleanliness:
- fewer file locks during install;
- a clear list of processes to restore after failure, instead of blindly restarting everything.
Check update ─→ stop local services (record stopped) ─→ download/install
│
fail ←─────────────────────────┴─────────────→ success → relaunch
│
└─ restore recorded services, then give an actionable message
Do not reverse this order. Installing first makes leftover processes more likely to race the installer for the same directory.
Separate failure branches instead of one generic error
At least three branches deserve distinct handling:
| Failure point | What the user needs to know | What the UI should not do |
|---|---|---|
| Check failed | network or endpoint issue; old version still usable | imply a new version is ready |
| Download/install interrupted | whether partial files were written; whether to retry | auto-restart into a half-written build |
| Installed but not restarted | new version is on disk; user should confirm restart | force-kill unsaved work silently |
Locally, a Tauri Updater check failure falls back to opening a manual installer link. That is not two parallel update strategies; it is one intent with a fallback, and the fallback still restores services that were stopped.
Version strings are not process state
“Up to date” only means the version comparison passed. It does not prove that:
- auxiliary processes from the last update have exited;
- temporary or
_up_-style upgrade directories were cleaned; - frontend assets loaded in the window match the executable version.
Failure messages should therefore carry checkable fields: current version, target version, failure stage, and whether local services were stopped. Start with those fields before choosing retry, manual install, or a cold start after clearing leftovers.
Test failure paths, not only successful upgrades
The happy path almost always passes. More useful cases:
- check endpoint timeout: keep the old version usable and label the error clearly;
- download interrupted: do not relaunch; restore stopped services;
- install rejected by locked files: tell the user what to close instead of silent retry loops;
- window recovery after identity switch or forced reconnect: rebuildable page state after restart.
Reliability of auto-update is measured first by whether the user still knows what state the app is in after a failure—and only then by whether one click installs the new version.