Skip to content

fix(desktop): bump electron-updater and recover from corrupt update cache#3495

Merged
Kitenite merged 2 commits into
mainfrom
desktop-autoupdater-cache-recovery
Apr 16, 2026
Merged

fix(desktop): bump electron-updater and recover from corrupt update cache#3495
Kitenite merged 2 commits into
mainfrom
desktop-autoupdater-cache-recovery

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented Apr 15, 2026

Summary

  • Bumps electron-updater from 6.7.36.8.3 (picks up 5 releases of staged-download and validation fixes).
  • Adds a defensive DownloadedUpdateHelper.clear() call on non-network error events so the next update check re-downloads from scratch.

Why

Users report auto-update sometimes never "comes through" — the app restarts on the same version and they have to manually reinstall to get updates working again.

electron-updater's internal cache at ~/Library/Caches/sh.superset.app-updater/pending/ only self-invalidates when the remote sha512 differs from the cached metadata. If a cached download is corrupt but its stored sha512 still matches the server's, the updater retries the same broken file on every cycle. Install-phase failures (code-signing, Squirrel ShipIt errors, hash mismatch on read) leave exactly this state.

Clearing the cache on error forces a fresh download on the next 4-hour cycle, so affected users recover without a manual reinstall.

This does not fix the initial install failure (almost always macOS notarization/signing). It only prevents the wedged-retry loop after one.

Test plan

  • Typecheck passes (bun run typecheck in apps/desktop) ✅
  • Manual: in a packaged build, trigger simulateError and confirm [auto-updater] Cleared cached update appears in logs
  • Manual: verify normal update flow still works end-to-end (check → download → install on quit)
  • Watch next release for user reports of the "stuck on old version" symptom

Summary by cubic

Prevents desktop auto-update from getting stuck by clearing a corrupt cached update when the updater errors, and upgrades electron-updater to 6.8.3 for download/validation fixes.

  • Bug Fixes

    • Clear cached update on any updater error so the next 4‑hour check re-downloads from scratch; logs clear/failure.
  • Dependencies

    • Bump electron-updater 6.7.3 → 6.8.3.

Written for commit 76c2c2c. Summary will update on new commits.

Summary by CodeRabbit

  • Chores
    • Updated the auto-update library dependency to the latest version.
  • Bug Fixes
    • Improved error handling and recovery mechanisms when application updates fail.

Bumps electron-updater from 6.7.3 to 6.8.3 and wires a defensive cache
clear into the error handler. Addresses reports of users stuck on an
old version until they manually reinstall: electron-updater's internal
cache only self-invalidates on remote sha512 mismatch, so a silently
corrupt cached download (failed install, signature error, Squirrel
ShipIt failure) would be retried indefinitely.

Non-network errors now call DownloadedUpdateHelper.clear() so the next
4-hour check re-downloads from scratch.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 15, 2026

📝 Walkthrough

Walkthrough

The pull request updates the electron-updater dependency from ^6.7.3 to ^6.8.3 and adds error handling logic that clears cached updates when auto-update operations fail.

Changes

Cohort / File(s) Summary
Dependency Update
apps/desktop/package.json
Bumped electron-updater version from ^6.7.3 to ^6.8.3.
Error Handling Enhancement
apps/desktop/src/main/lib/auto-updater.ts
Added internal cache clearing mechanism for electron-updater. Introduced downloadedUpdateHelper interface and clearCachedUpdate() async helper function. When autoUpdater emits an error event, the cache is cleared before emitting AUTO_UPDATE_STATUS.ERROR.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 A nibbling update, oh so sweet,
With error handling, more complete!
When caches clear with gentle care,
Updates dance through autumn air! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes both main changes: bumping electron-updater and adding cache recovery logic on update errors.
Description check ✅ Passed The description includes all key sections: Summary, Why/Motivation, Test plan with checkboxes, and supplementary auto-generated details covering the changes comprehensively.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch desktop-autoupdater-cache-recovery

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 15, 2026

Greptile Summary

This PR fixes the "stuck on old version" auto-update regression in the desktop app by bumping electron-updater from 6.7.3^6.8.3 and adding a defensive DownloadedUpdateHelper.clear() call on non-network error events to break the corrupt-cache retry loop.

  • Version bump (apps/desktop/package.json): electron-updater ^6.8.3 picks up 5 patch releases of staged-download and validation fixes upstream.
  • Cache recovery (auto-updater.ts): A new clearCachedUpdate(reason) helper accesses the protected downloadedUpdateHelper property via a documented type-cast (internally confirmed valid in electron-updater's source) and awaits helper.clear(), which empties ~/Library/Caches/sh.superset.app-updater/pending/. This is called inside the autoUpdater.on("error", …) handler for any error that is not classified as a transient network error, so the next 4-hour check cycle starts with a clean slate.
  • Null guard + try/catch: clearCachedUpdate returns early if downloadedUpdateHelper is null (no download in progress) and wraps the clear() call in a try/catch, so a failure here cannot crash the updater or block the error status emission.
  • Test plan note: The test plan step that asks to "trigger simulateError in a packaged build" cannot validate the new code path — simulateError is dev-only and only calls emitStatus, never the autoUpdater error event. The cache-clearing path can only be validated by a real install-phase failure in a packaged build.

Confidence Score: 4/5

Safe to merge — the cache-clearing logic is correctly placed, properly guarded, and the internal API access is confirmed valid against electron-updater source.

The fix is well-reasoned and minimally invasive: a null-checked, try/caught fire-and-forget call on a confirmed internal property. The only concern is the inaccurate test plan step (simulateError cannot validate the new code path), which is documentation rather than a code defect.

No files require special attention; the test plan in the PR description should be corrected to describe a valid validation path for the cache-clearing behavior.

Important Files Changed

Filename Overview
apps/desktop/src/main/lib/auto-updater.ts Adds clearCachedUpdate helper that casts to internal DownloadedUpdateHelper (confirmed valid, protected property in electron-updater) and calls it on non-network errors; the simulateError dev helper cannot actually exercise this new path.
apps/desktop/package.json Bumps electron-updater from 6.7.3 to ^6.8.3; all other deps unchanged.

Sequence Diagram

sequenceDiagram
    participant Timer as 4-hr Timer
    participant AU as autoUpdater
    participant DUH as DownloadedUpdateHelper
    participant Cache as Pending Cache Dir

    Timer->>AU: checkForUpdates()
    AU->>AU: emit "checking-for-update"
    AU->>AU: emit "update-available"
    AU->>AU: emit "download-progress"
    AU->>DUH: save download + metadata (sha512)
    AU->>AU: emit "update-downloaded"

    Note over AU,Cache: Install phase (on quit or manual)
    AU->>AU: quitAndInstall()

    alt Install succeeds
        AU-->>AU: (new version running)
    else Install fails (signing/hash/Squirrel error)
        AU->>AU: emit "error"
        AU->>DUH: clearCachedUpdate() [NEW]
        DUH->>Cache: emptyDir(cacheDirForPendingUpdate) [NEW]
        Note over Cache: Cache cleared — next cycle re-downloads from scratch [NEW]
        AU->>AU: emitStatus(ERROR)
    end
Loading

Comments Outside Diff (1)

  1. apps/desktop/src/main/lib/auto-updater.ts, line 216-224 (link)

    P2 simulateError won't validate the cache-clearing path

    The test plan says: "in a packaged build, trigger simulateError and confirm [auto-updater] Cleared cached update appears in logs." This step cannot work as described for two independent reasons:

    1. simulateError guards with if (env.NODE_ENV !== "development") return; — it is a no-op in every packaged/production build.
    2. Even in dev mode, simulateError only calls emitStatus(...). It does not emit an error event on autoUpdater, so the autoUpdater.on("error", ...) listener (where clearCachedUpdate is invoked) is never triggered.

    Additionally, setupAutoUpdater itself returns early in dev mode, so the "error" event listener is never registered there either.

    To actually validate the clearCachedUpdate path you would need to either (a) emit a synthetic error event on the autoUpdater instance in a dev hook, or (b) test in a real packaged build where a corrupt cache triggers the native error event. The test plan step should be updated to reflect a realistic validation approach.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/desktop/src/main/lib/auto-updater.ts
    Line: 216-224
    
    Comment:
    **`simulateError` won't validate the cache-clearing path**
    
    The test plan says: *"in a packaged build, trigger `simulateError` and confirm `[auto-updater] Cleared cached update` appears in logs."* This step cannot work as described for two independent reasons:
    
    1. `simulateError` guards with `if (env.NODE_ENV !== "development") return;` — it is a no-op in every packaged/production build.
    2. Even in dev mode, `simulateError` only calls `emitStatus(...)`. It does **not** emit an `error` event on `autoUpdater`, so the `autoUpdater.on("error", ...)` listener (where `clearCachedUpdate` is invoked) is never triggered.
    
    Additionally, `setupAutoUpdater` itself returns early in dev mode, so the `"error"` event listener is never registered there either.
    
    To actually validate the `clearCachedUpdate` path you would need to either (a) emit a synthetic `error` event on the `autoUpdater` instance in a dev hook, or (b) test in a real packaged build where a corrupt cache triggers the native error event. The test plan step should be updated to reflect a realistic validation approach.
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: apps/desktop/src/main/lib/auto-updater.ts
Line: 216-224

Comment:
**`simulateError` won't validate the cache-clearing path**

The test plan says: *"in a packaged build, trigger `simulateError` and confirm `[auto-updater] Cleared cached update` appears in logs."* This step cannot work as described for two independent reasons:

1. `simulateError` guards with `if (env.NODE_ENV !== "development") return;` — it is a no-op in every packaged/production build.
2. Even in dev mode, `simulateError` only calls `emitStatus(...)`. It does **not** emit an `error` event on `autoUpdater`, so the `autoUpdater.on("error", ...)` listener (where `clearCachedUpdate` is invoked) is never triggered.

Additionally, `setupAutoUpdater` itself returns early in dev mode, so the `"error"` event listener is never registered there either.

To actually validate the `clearCachedUpdate` path you would need to either (a) emit a synthetic `error` event on the `autoUpdater` instance in a dev hook, or (b) test in a real packaged build where a corrupt cache triggers the native error event. The test plan step should be updated to reflect a realistic validation approach.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "chore(desktop): tighten auto-updater cac..." | Re-trigger Greptile

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 15, 2026

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch
  • ✅ Electric Fly.io app

Thank you for your contribution! 🎉

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 3 files

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
apps/desktop/src/main/lib/auto-updater.ts (1)

18-22: Add a warning when downloadedUpdateHelper is unavailable for better observability.

downloadedUpdateHelper is a protected (not public) property in electron-updater v6.8.3 and remains protected across 6.x versions. Accessing it via unsafe casting is necessary but fragile—if future versions alter the property, the silent return hides the failure. Adding a console warning when the helper is undefined will make cache-clear regressions diagnosable.

Proposed change
 async function clearCachedUpdate(reason: string): Promise<void> {
 	const helper = (autoUpdater as unknown as AppUpdaterInternals)
 		.downloadedUpdateHelper;
-	if (!helper) return;
+	if (!helper) {
+		console.warn(
+			"[auto-updater] downloadedUpdateHelper unavailable; skipping cache clear",
+		);
+		return;
+	}
 	try {
 		await helper.clear();
 		console.info(`[auto-updater] Cleared cached update (${reason})`);
 	} catch (error) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/main/lib/auto-updater.ts` around lines 18 - 22,
clearCachedUpdate silently returns when the protected downloadedUpdateHelper
(accessed via (autoUpdater as unknown as
AppUpdaterInternals).downloadedUpdateHelper) is missing; update
clearCachedUpdate to log a warning before returning so missing helper cases are
observable. Specifically, inside clearCachedUpdate check the helper variable and
if undefined call console.warn (or the existing logger) with a clear message
referencing downloadedUpdateHelper and the provided reason, then return; keep
the existing try/catch behavior and error handling for the rest of the function.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/desktop/src/main/lib/auto-updater.ts`:
- Around line 18-22: clearCachedUpdate silently returns when the protected
downloadedUpdateHelper (accessed via (autoUpdater as unknown as
AppUpdaterInternals).downloadedUpdateHelper) is missing; update
clearCachedUpdate to log a warning before returning so missing helper cases are
observable. Specifically, inside clearCachedUpdate check the helper variable and
if undefined call console.warn (or the existing logger) with a clear message
referencing downloadedUpdateHelper and the provided reason, then return; keep
the existing try/catch behavior and error handling for the rest of the function.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 34b23600-11db-4fca-ba41-dad56a78d464

📥 Commits

Reviewing files that changed from the base of the PR and between 86e0170 and 76c2c2c.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (2)
  • apps/desktop/package.json
  • apps/desktop/src/main/lib/auto-updater.ts

@Kitenite Kitenite merged commit c2e96c3 into main Apr 16, 2026
15 checks passed
@Kitenite Kitenite deleted the desktop-autoupdater-cache-recovery branch April 16, 2026 00:39
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.

1 participant