Skip to content

fix(electron-updater): AppImage install no longer deletes the running app before the new one is in place - #10386

Merged
mmaietta merged 6 commits into
electron-userland:masterfrom
Derpedyea:fix/appimage-atomic-install
Oct 24, 2026
Merged

mmaietta merged 6 commits into
electron-userland:masterfrom
Derpedyea:fix/appimage-atomic-install

Conversation

@Derpedyea

@Derpedyea Derpedyea commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Problem

AppImageUpdater.doInstall deletes the running AppImage first (unlinkSync(appImageFile)) and only then runs execFileSync("mv", ["-f", installerPath, destination]), which resolves mv through PATH. If mv fails, the old AppImage is already gone and the user has no app to launch.

Real-world report: pingdotgg/t3code#23188. mv failed with spawnSync mv ENOTCONN because the dead FUSE mount dir ($TMPDIR/.mount_*) of an earlier AppImage launch was on PATH. The next install attempt then failed with ENOENT on the unlink, and the app could not be launched (exit 127).

Related: #8698 reports the same failure: the old AppImage was removed, then mv failed to move the download.

Fix

  • All validation (APPIMAGE checks, missing installerPath) now runs before any file is touched. The unlink-first step is gone.
  • The destination rule is unchanged (Autoupdate AppImage renames file #2964: a versioned file name follows the download, a custom name is kept).
  • The new AppImage is staged as .<old name>.update next to the destination (linkSync, falling back to copyFileSync on EXDEV or a filesystem without hard links), then renameSyncd over the destination. That swap is atomic and runs no external command. On any error the staged file is removed and the error is rethrown, so the old AppImage stays in place.
  • After a successful swap the downloaded file is removed (as mv did). When the file name changed, the old AppImage is removed and appimage-filename-updated is emitted, as before.
  • The relaunch logic is unchanged.

Before: delete the old app then mv; if mv fails no app is left. After: copy next to the old app then rename; if anything fails the old app still opens.

Verification

Two new tests in test/src/updater/baseUpdaterUnitTest.ts (AppImageUpdater doInstall file replacement):

  1. Install succeeds with PATH pointed at an empty dir, and the AppImage holds the new content.
  2. When the swap fails (no downloaded file), the old AppImage is unchanged.

On master (without the fix), both fail:

× replaces the AppImage without a PATH lookup
Error: spawnSync mv ENOENT

× keeps the installed AppImage when the swap fails
Error: ENOENT: no such file or directory, open '.../TestApp.AppImage'

With the fix:

  • TEST_FILES=baseUpdaterUnitTest,linuxUpdaterUnitTest pnpm ci:test: 2 files, 67 tests passed
  • pnpm compile && pnpm ci:validate: passed
  • pnpm format:check: passed

A changeset (electron-updater: patch) is included.

🤖 Generated with Claude Code

… app before the new one is in place

doInstall unlinked the running AppImage first and then ran `mv` through
PATH. When `mv` failed (e.g. `spawnSync mv ENOTCONN` from a dead AppImage
FUSE mount left on PATH), the old AppImage was already gone and the app
could no longer be launched.

Stage the new AppImage next to the destination (hard link, falling back to
a copy) and rename it over the destination with fs calls only. A failure
leaves the old AppImage untouched and removes the staged file.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@changeset-bot

changeset-bot Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 38249b6

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
electron-updater Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@Derpedyea

Copy link
Copy Markdown
Contributor Author

The three failed jobs (Test Linux shard 6, Test Snap core22, e2e Auto Updater Linux debian/dpkg) all broke on registry downloads: HTTP 503 or a timeout fetching npm/pnpm through corepack. None of them touch `AppImageUpdater`, and Test Updater passed.

npm has an open incident for this, Degraded Package Install and Publishing, which started at 02:33 UTC, before these jobs ran. Could a maintainer re-run the failed jobs once it's resolved?

Comment thread test/src/updater/baseUpdaterUnitTest.ts Resolved
@mmaietta
mmaietta merged commit 8512f4d into electron-userland:master Oct 24, 2026
57 of 58 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants