Desktop app self-update reliability across platforms

NousResearch/hermes-agent · cluster 37557 · apps/desktop

Data as of

4 open items. Fix merged in #50448. Check it covers your case.

The short answer

3open issues
1open PR
13items grouped
Known?
Yes. 13 related items grouped here, 6 issues already closed.
Fix event
A fix was merged to main in PR #50448. Check it covers your case. No release data is tracked.
Tracker
The maintainers' tracker marks this partially resolved, deciding PRs #50448, #86675. That is separate from the fix event.
Still open
3 issues and 1 PR.

These issues and pull requests address bugs and improvements in the desktop app's in-app self-update flow on both macOS and Windows, covering problems such as silent failures, update loops, missing progress feedback, and unreliable updater handoff.

Open issues3

  • Issue#39248[Bug]: Desktop app update process is broken
  • Issue#46076Hermes Desktop in-app update does not reliably apply or relaunch on macOS
  • Issue#64577[Bug]: macOS desktop self-update fails four ways on long-lived installs (stale staged updater, silent backup, canonical-app desync, retry-loop errors)

Open PRs1

  • PR#57763fix: macOS in-app update silent failure handling (#57645)

Fixes on record

Merged to main. A stamp counts the issues a fix is linked to.

  • #50448 feat(desktop/windows): show update-in-progress feedback before the desktop exits

    merged to main

    files: apps/desktop/electron/main.cjs, apps/desktop/src/i18n/en.ts, apps/desktop/src/i18n/ja.ts, apps/desktop/src/i18n/zh-hant.ts, apps/desktop/src/i18n/zh.ts

    Show the diff
    • apps/desktop/electron/main.cjs
      @@ -1129,6 +1129,14 @@ function directoryExists(filePath) {
       // marker's own age ceiling; covers a stuck-but-alive updater).
       const UPDATE_WAIT_TIMEOUT_MS = 20 * 60 * 1000
       const UPDATE_WAIT_POLL_MS = 1000
      +// How long the desktop lingers on the "updating, don't reopen" overlay after
      +// spawning the detached updater, before it quits to release the venv shim. The
      +// old 600ms was long enough to register the child process but far too short for
      +// the user to READ the overlay — the window just vanished, looked like a crash,
      +// and the user relaunched mid-update (the #50238 restart-loop trigger). A
      +// couple of seconds lets the message land and bridges the gap until the
      +// updater's own progress window appears. (#50419)
      +const UPDATE_HANDOFF_DWELL_MS = 2500
       
       // Block until no live update is in progress (or we hit the wait timeout).
       // Emits a boot-progress phase so the renderer shows "Update in progress…"
      @@ -1867,7 +1875,11 @@ async function applyUpdates(opts = {}) {
             return { ok: true, manual: true, command, hermesRoot: updateRoot }
           }
       
      -    emitUpdateProgress({ stage: 'restart', message: 'Handing off to the Hermes updater…', percent: 100 })
      +    emitUpdateProgress({
      +      stage: 'restart',
      +      message: 'Updating Hermes — this window will close and the updater will open. Don’t reopen Hermes yourself; it restarts automatically when the update finishes.',
      +      percent: 100
      +    })
           repairMacUpdaterHelper(updater)
       
           const updateRoot = resolveUpdateRoot()
      @@ -1903,11 +1915,14 @@ async function applyUpdates(opts = {}) {
       
           rememberLog(`[updates] launched updater: ${updater} ${updaterArgs.join(' ')}; exiting desktop to release venv shim`)
       
      -    // Give the OS a beat to register the new process, then quit. The updater
      -    // rebuilds and relaunches us when it's done.
      +    // Linger on the "updating — don't reopen" overlay long enough for the user
      +    // to actually read it (and to bridge the gap until the updater's own window
      +    // appears), THEN quit to release the venv shim. The updater rebuilds and
      +    // relaunches us when it's done. (#50419 — a 600ms quit looked like a crash
      +    // and lured users into the #50238 relaunch loop.)
           setTimeout(() => {
             app.quit()
      -    }, 600)
      +    }, UPDATE_HANDOFF_DWELL_MS)
       
           return { ok: true, handedOff: true, updater }
         } finally {
      @@ -1946,9 +1961,12 @@ async function handOffWindowsBootstrapRecovery(reason) {
         child.unref()
       
         rememberLog(`[bootstrap] handed off ${reason} recovery to updater: ${updater} ${updaterArgs.join(' ')}; exiting desktop to release app.asar`)
      +  // Same dwell as the in-app update hand-off (#50419): give the updater's
      +  // window time to appear before we vanish, so the recovery doesn't look like
      +  // a crash and provoke a mid-recovery relaunch.
         setTimeout(() => {
           app.quit()
      -  }, 600)
      +  }, UPDATE_HANDOFF_DWELL_MS)
       
         return true
       }
    • apps/desktop/src/i18n/en.ts
      @@ -1382,10 +1382,11 @@ export const en: Translations = {
           copy: 'Copy',
           copied: 'Copied',
           done: 'Done',
      -    applyingBody: 'The Hermes updater will take over in its own window and reopen Hermes when it’s done.',
      +    applyingBody:
      +      'The Hermes updater takes over in its own window and reopens Hermes automatically when it’s done. Please don’t reopen Hermes yourself while it’s updating.',
           applyingBodyBackend:
             'The remote backend is applying the update and will restart. Hermes reconnects automatically when it’s back.',
      -    applyingClose: 'Hermes will close to apply the update.',
      +    applyingClose: 'This window will close while the update runs, then Hermes reopens on its own.',
           errorTitle: 'Update didn’t finish',
           errorBody: 'No worries — nothing was lost. You can try again now.',
           notNow: 'Not now',
    • apps/desktop/src/i18n/ja.ts
      @@ -1512,9 +1512,9 @@ export const ja = defineLocale({
           copy: 'コピー',
           copied: 'コピーしました',
           done: '完了',
      -    applyingBody: 'Hermes アップデーターが独自のウィンドウで引き継ぎ、完了後に Hermes を再度開きます。',
      +    applyingBody: 'Hermes アップデーターが独自のウィンドウで引き継ぎ、完了後に自動的に Hermes を再度開きます。更新中はご自分で Hermes を開き直さないでください。',
           applyingBodyBackend: 'リモートバックエンドが更新を適用して再起動します。復帰すると Hermes が自動的に再接続します。',
      -    applyingClose: 'Hermes は更新を適用するために閉じます。',
      +    applyingClose: 'このウィンドウは更新中に閉じ、その後 Hermes が自動的に再度開きます。',
           errorTitle: '更新が完了しませんでした',
           errorBody: 'ご安心ください。何も失われていません。今すぐ再試行できます。',
           notNow: '今は後で',
    • apps/desktop/src/i18n/zh-hant.ts
      @@ -1463,9 +1463,9 @@ export const zhHant = defineLocale({
           copy: '複製',
           copied: '已複製',
           done: '完成',
      -    applyingBody: 'Hermes 更新程式會在自己的視窗中接管,並在完成後重新開啟 Hermes。',
      +    applyingBody: 'Hermes 更新程式會在自己的視窗中接管,並在完成後自動重新開啟 Hermes。更新期間請勿自行重新開啟 Hermes。',
           applyingBodyBackend: '遠端後端正在套用更新並將重新啟動。恢復後 Hermes 會自動重新連線。',
      -    applyingClose: 'Hermes 將關閉以套用更新。',
      +    applyingClose: '此視窗會在更新期間關閉,隨後 Hermes 會自動重新開啟。',
           errorTitle: '更新未完成',
           errorBody: '沒有資料遺失。您可以現在重試。',
           notNow: '暫不',
    • apps/desktop/src/i18n/zh.ts
      @@ -1568,9 +1568,9 @@ export const zh: Translations = {
           copy: '复制',
           copied: '已复制',
           done: '完成',
      -    applyingBody: 'Hermes 更新器会在自己的窗口中接管,并在完成后重新打开 Hermes。',
      +    applyingBody: 'Hermes 更新器会在自己的窗口中接管,并在完成后自动重新打开 Hermes。更新期间请不要自行重新打开 Hermes。',
           applyingBodyBackend: '远程后端正在应用更新并将重启。恢复后 Hermes 会自动重新连接。',
      -    applyingClose: 'Hermes 将关闭以应用更新。',
      +    applyingClose: '此窗口会在更新期间关闭,随后 Hermes 会自动重新打开。',
           errorTitle: '更新未完成',
           errorBody: '没有数据丢失。你可以现在重试。',
           notNow: '暂不',
  • #86675 fix(desktop): confirm detached updater spawn before quitting for hand-off (#66753)

    merged to main

    files: apps/desktop/electron/main.ts, apps/desktop/electron/updater-process.test.ts, apps/desktop/electron/updater-process.ts

1 PR closed without merging

From the close comments: other 1

  • #42121 fix(desktop): stop macOS Update & Restart loop by always updating in-app · other
Closed issues · 6
  • Closed#37557[Bug]: Windows desktop: /update from chat silently hangs — no progress/logs, updater handoff not observable
  • Closed#39339[Bug]: Desktop app update installs but version remains unchanged — update loop on macOS
  • Closed#43531[Bug]: hermes desktop errors out when updating
  • Closed#50419feat(desktop/windows): show update progress before the desktop exits so it doesn't look like a crash
  • Closed#57645[Bug]: desktop in-app update on macOS closes without updating
  • Closed#66753[Bug]: Desktop exits without confirming detached updater spawn or early process survival
How this cluster was grouped
  • Link evidence (partly structural links): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text.
  • Members outside that structural core may belong to a different underlying problem.
  • Pairwise link density 0.12: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. 1.00 means every pair is linked.

Pick up a Lab task

Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.

Open the Lab

At a glance

Subsystem
apps/desktop
Merged PRs
#50448, #86675
Closed issues
6
Attempts
1 PR closed without merging
Fix link checked
15 Aug 2026

For your agent

The same facts, typed.