Skip to content

Comments

Stabilize updater check/download flow and remove duplicate auto-download trigger#965

Open
rabanspiegel wants to merge 7 commits intomainfrom
emdash/update-path-6ta
Open

Stabilize updater check/download flow and remove duplicate auto-download trigger#965
rabanspiegel wants to merge 7 commits intomainfrom
emdash/update-path-6ta

Conversation

@rabanspiegel
Copy link
Contributor

@rabanspiegel rabanspiegel commented Feb 19, 2026

Summary

  • fix updater state race where download could fail with status=checking during overlapping checks
  • dedupe in-flight update checks and keep available/downloading/downloaded state from being clobbered by transient checking events
  • make download path idempotent for duplicate triggers and permit download when a known available version exists
  • avoid modal-side duplicate auto-download when backend auto-download is enabled
  • harden install/restart flow with an explicit installing state and single-shot quitAndInstall guard
  • guard stale update-not-available events from clobbering active available/downloading/downloaded/installing state

Validation

  • pnpm run type-check
  • pnpm exec vitest run
  • pnpm run format

Closes #966


Note

Medium Risk
Touches the app’s update lifecycle (check/download/install) and IPC-driven state transitions; failures could block updates or leave the UI in an incorrect state, though changes are localized to updater codepaths.

Overview
Improves updater robustness by deduping concurrent update checks and preventing transient checking/not-available/error events from overwriting a known available/downloading/downloaded/installing state, including more controlled user-visible error reporting.

Makes update actions more resilient: downloadUpdate() becomes idempotent and can proceed when an update version is already known even if a check is still running, and quitAndInstall() is single-shot with a timeout guard/rollback plus a new installing status broadcast from main to renderer.

Updates renderer UX to handle installing across useUpdater, UpdateCard, and UpdateModal, and avoids modal-side duplicate auto-download by first reading backend autoDownload settings before triggering its own download behavior.

Written by Cursor Bugbot for commit 1c0101f. This will update automatically on new commits. Configure here.

@vercel
Copy link

vercel bot commented Feb 19, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview, Comment Feb 20, 2026 0:13am

Request Review

@greptile-apps
Copy link

greptile-apps bot commented Feb 19, 2026

Greptile Summary

This PR stabilizes the updater check/download flow by addressing race conditions and duplicate triggers. The changes deduplicate concurrent update checks using a promise-based lock (checkInFlight), prevent transient checking events from clobbering stable states like available/downloading/downloaded/installing, and make downloadUpdate() idempotent to handle duplicate button clicks. The PR also fixes a race where downloads could fail if triggered during an overlapping check by allowing downloads when status=checking but a cached availableVersion exists. On the UI side, the modal now fetches backend update settings to avoid duplicate auto-download when backend autoDownload is enabled, and treats backend checking as a valid state to reflect rather than forcing a new check. A new installing state has been added throughout the stack to provide better user feedback during the final install phase.

Key improvements:

  • Deduplicates in-flight checks via promise reuse
  • Prevents state clobbering from transient checking events
  • Makes download idempotent for duplicate triggers
  • Fixes race where download fails during overlapping checks
  • Avoids modal-side duplicate auto-download by checking backend settings
  • Adds installing state for clearer UX during quit-and-install

Confidence Score: 4/5

  • This PR is safe to merge with low-to-moderate risk
  • The changes are defensive and improve stability without introducing new features. The code is well-structured with proper error handling and state management. The risk is slightly elevated because this touches critical update infrastructure where bugs could prevent users from getting updates, but the changes are logical and address real race conditions. The idempotency checks and state guards reduce the risk of unexpected behavior.
  • Pay close attention to src/main/services/AutoUpdateService.ts as it contains the core state management logic

Important Files Changed

Filename Overview
src/main/services/AutoUpdateService.ts Added deduplication for concurrent checks, made download idempotent, prevented state clobbering from transient checking events, and added installing state
src/renderer/components/UpdateModal.tsx Fixed duplicate auto-download by checking backend settings, now treats checking as a valid backend state to reflect, added installing UI state
src/renderer/hooks/useUpdater.ts Added installing state to type union and event handling, improved error handling in install function
src/main/preload.ts Added update:installing event channel to onUpdateEvent listener registration
src/renderer/components/UpdateCard.tsx Added installing state UI rendering and disabled check button during install

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User Opens Update Modal] --> B{Backend State Check}
    B -->|checking/downloading/downloaded/installing| C[Apply Backend State]
    B -->|available| D[Reflect Available State]
    B -->|other| E[Trigger Fresh Check]
    
    E --> F{Check In Flight?}
    F -->|Yes| G[Return Existing Promise]
    F -->|No| H[Perform Check]
    
    H --> I{Update Available?}
    I -->|Yes| J[Set status=available]
    I -->|No| K[Set status=idle]
    
    J --> L{Modal Auto-Download Enabled?}
    L -->|Backend autoDownload=true| M[Skip Modal Download]
    L -->|Backend autoDownload=false| N[Trigger Download]
    
    N --> O{Download Status?}
    O -->|downloading/downloaded| P[Return Early - Idempotent]
    O -->|checking with availableVersion| Q[Allow Download - Race Fix]
    O -->|available| R[Start Download]
    
    R --> S[Update Downloaded]
    S --> T[User Clicks Install]
    T --> U{Already Installing?}
    U -->|Yes| V[Ignore Request]
    U -->|No| W[Set status=installing]
    W --> X[quitAndInstall]
    
    style Q fill:#90EE90
    style P fill:#90EE90
    style G fill:#90EE90
    style M fill:#90EE90
Loading

Last reviewed commit: 9ff91ca

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

});

autoUpdater.on('update-not-available', (info: UpdateInfo) => {
// A stale "not available" result from a concurrent check must not overwrite
Copy link

Choose a reason for hiding this comment

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

Missing guard lets update-available clobber installing state

Medium Severity

The update-available handler unconditionally sets status = 'available', while the sibling handlers for checking-for-update, update-not-available, and error all guard against clobbering downloading/downloaded/installing states. If a check was initiated before an install began (e.g., a periodic check whose checkForUpdatesAndNotify was already in flight), the late-arriving update-available event will overwrite the installing state with available, defeating the new single-shot install guard and rollback timer.

Additional Locations (2)

Fix in Cursor Fix in Web

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.

[bug]: auto-updater made Emdash hang/crash but installed successfully

1 participant