Skip to content

AppImage auto-update leaves a 0-byte binary and the app never launches again #10685

Description

@egefeyzioglu

What happened

When I launch T3 Code using the AppImage nothing happens, it doesn't open. If I run with a terminal it returns immediately with exit code 0 (what I ran was <appimage-dir>/t3code.AppImage --no-sandbox)

The desktop app had auto-updated to 0.0.41-nightly.20260908.1377 a few hours earlier. The user never saw an error — the app simply quit to install and never came back.

Diagnosis

The auto-update replaced the user's AppImage with a 0-byte file and left it that way. bash cannot execve an empty file, so it falls back to running it as a shell script; an empty script exits 0 immediately, which is exactly the reported symptom.

-rwxr-xr-x 1 <user> <user> 0 Sep  8 01:37 <appimage-dir>/t3code.AppImage

Timeline reconstructed from logs/desktop.trace.ndjson plus inode timestamps (all times local, -0400):

Time Event
01:14:36 app start, channel nightly (allowPrerelease=true, allowDowngrade=true)
01:14:51 → 01:34:38 update available 0.0.41-nightly.20260908.1377 on every poll
01:36:57.996 downloading update
01:37:09.338 download progress 100%
01:37:09.344 pending/update-info.json written (sha512 verified, download intact)
01:37:09.346 update downloadedlast updater log line anywhere
01:37:09.848 trace ends; app quits to install
01:37:14.162 t3code.AppImage re-created (birth), mtime copied from the downloaded file (01:37:09.339), mode 0755, size 0
01:37:14.249 downloaded AppImage removed from ~/.cache/t3code-updater/pending/ (only update-info.json remains)
no further run appears in the trace: the post-install relaunch never happened

The download itself was fine — it reached 100%, passed electron-updater's sha512 check, and update-info.json was written. The file was destroyed during install.

apps/desktop/src/updates/DesktopUpdates.ts calls quitAndInstall({ isSilent: true, isForceRunAfter: true }) and apps/desktop/src/electron/ElectronUpdater.ts is a thin wrapper over stock electron-updater (6.8.3 per pnpm-lock.yaml), so the install is AppImageUpdater.doInstall:

unlinkSync(appImageFile);                                  // working AppImage deleted FIRST
...
if (basename(installerPath) === existingBaseName || !/\d+\.\d+\.\d+/.test(existingBaseName)) {
  destination = appImageFile;                              // no x.y.z in the name → overwrite in place
}
execFileSync("mv", ["-f", installerPath, destination]);     // result never verified

Two things combine here:

  1. The user's file is named t3code.AppImage. It contains no x.y.z, so doInstall takes the overwrite-in-place branch rather than writing a new versioned file beside it.
  2. ~/.cache/t3code-updater and the AppImage are on different filesystems (/ vs a second NVMe mounted at /mnt/storage), so mv degrades from an atomic rename(2) to copy-then-delete. The copy produced 0 bytes; the preserved mtime and 0755 mode show mv ran to completion and reported success.

I could not reproduce a bad mv — three full-size (186 MB) copies across the same source→destination pair came out byte-exact — so the reason that one copy came out empty is not established. Environment notes that may matter: / is at 99% (2.6 GB free), and there was no ENOSPC in any log and no I/O or filesystem error in the kernel journal around 01:37.

What turns a bad copy into a bricked install is the surrounding design, and that part is independent of the root cause:

  • the working AppImage is unlinked before the replacement is in place, so there is nothing to fall back to;
  • nothing verifies the installed file's size or sha512 against the artifact that was just downloaded and verified;
  • the app has already quit, so no failure can be surfaced to the user;
  • the install step produces no log line at all. DesktopUpdates.ts logs update downloaded and then calls quitAndInstall with no logging around it, and electron-updater's own logger is not wired into the trace. On this machine desktop-main.log, which used to capture electron-updater's stdout, was last written 2026-05-11 — so the entire install step is invisible after the fact.

Suggested directions, roughly in order of value:

  • stage the downloaded artifact on the same filesystem as $APPIMAGE (e.g. a dot-prefixed temp file in dirname($APPIMAGE)) so the swap is an atomic rename(2) and a partial copy is impossible;
  • verify the installed file (size + sha512 against update-info.json) before relaunching, and restore/abort if it does not match;
  • keep the old AppImage until the new one is verified, instead of unlinkSync first;
  • log the install step and wire autoUpdater.logger into the trace so the next occurrence leaves evidence.

Steps to reproduce

The bricking is deterministic given a failed copy; the failing copy itself was not reproducible on demand. Reproducing the fragile path:

  1. On Linux, put the AppImage on a filesystem different from the one holding ~/.cache/t3code-updater, and give it a filename with no x.y.z version in it (e.g. t3code.AppImage).
  2. Launch it and let the nightly channel find an update.
  3. Click install. electron-updater unlinks the AppImage and mvs the downloaded file across the filesystem boundary onto that exact path.
  4. If that copy is short or empty for any reason, the app quits, never relaunches, and the AppImage is left unusable with no error and no log entry after update downloaded.

To confirm the symptom directly, without the updater: : > t3code.AppImage && ./t3code.AppImage --no-sandbox; echo $? → prints 0, no window, no output.

Version

0.0.40 (CLI); desktop app was on the nightly channel updating to 0.0.41-nightly.20260908.1377

Environment

Ubuntu 24.04.4 LTS, kernel 6.8.0-139-generic, x86_64, Node v24.15.0, electron 43.4.1, electron-updater 6.8.3, GNU coreutils 9.4 (mv). AppImage and updater cache on separate ext4 filesystems; root filesystem 99% full (2.6 GB free).

Evidence

# logs/desktop.trace.ndjson — updater events, last run before the app disappeared
01:14:36.466 INFO desktop-updater | using update channel | {"channel":"nightly","allowPrerelease":true,"allowDowngrade":true,"fullChangelog":true}
01:34:38.789 INFO desktop-updater | update available   | {"version":"0.0.41-nightly.20260908.1377","releaseNoteGroups":6,"omittedReleaseCount":4}
01:36:57.996 INFO desktop-updater | downloading update | {"channel":"desktop:update-download"}
01:37:09.289 INFO desktop-updater | download progress  | {"percent":99}
01:37:09.338 INFO desktop-updater | download progress  | {"percent":100}
01:37:09.346 INFO desktop-updater | update downloaded  | {"version":"0.0.41-nightly.20260908.1377"}
# ...trace ends 01:37:09.848. No install log line, no error, no subsequent run.

# the installed binary afterwards
$ stat <appimage-dir>/t3code.AppImage
  Size: 0          Blocks: 0          IO Block: 4096   regular empty file
Access: (0755/-rwxr-xr-x)
Modify: 2026-09-08 01:37:09.339021853 -0400   <- copied from the downloaded artifact
Change: 2026-09-08 01:37:14.249055832 -0400
 Birth: 2026-09-08 01:37:14.162055238 -0400   <- created by `mv` during doInstall

$ file <appimage-dir>/t3code.AppImage
<appimage-dir>/t3code.AppImage: empty

# the downloaded artifact was intact and is now gone (moved by doInstall)
$ ls -la ~/.cache/t3code-updater/pending/
-rw-rw-r-- 1 ... 198 Sep  8 01:37 update-info.json      # dir mtime 01:37:14.249
$ cat ~/.cache/t3code-updater/pending/update-info.json
{"fileName":"T3-Code-0.0.41-nightly.20260908.1377-x86_64.AppImage","sha512":"Ip8hDFqjC+5pcl...","isAdminRightsRequired":false}
# release asset size for that build: 186,531,181 bytes

# separate filesystems for cache and target
/dev/nvme0n1p1  234G  220G  2.6G  99% /            <- ~/.cache/t3code-updater
/dev/nvme1n1p5  871G  611G  217G  74% /mnt/...     <- the AppImage

Related issues

#9945 / #9946 (closed, not merged) and #8944 / #8983 all concern AppImage filenames across updates, but they are about dock pins and desktop entries breaking, not about the update destroying the binary. No open or closed issue covers an update leaving a 0-byte AppImage. Nothing on main since v0.0.40 touches the install path, so this is not fixed in a newer release.

Fix applied or workaround

Re-downloaded T3-Code-0.0.41-nightly.20260908.1387-x86_64.AppImage onto the same filesystem as the target, verified its sha512 against nightly-linux.yml, then mv'd it into place and chmod 755. Confirmed working: the app booted, backend ready and main window created appear in the trace, and the updater reports no updates available. A verified backup copy was left beside it, since the next auto-update can hit the same path.

Filed by

claude (Opus 5, 1M context) via t3 triage

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    acceptedfeature request acceptedbugSomething is broken or behaving incorrectly.upstreamvia-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions