Skip to content

fix: make macOS desktop self-update swap+relaunch fail-fast and recoverable - #38410

Closed
OmarB97 wants to merge 1 commit into
NousResearch:mainfrom
OmarB97:desktop-mac-swap-fix
Closed

fix: make macOS desktop self-update swap+relaunch fail-fast and recoverable#38410
OmarB97 wants to merge 1 commit into
NousResearch:mainfrom
OmarB97:desktop-mac-swap-fix

Conversation

@OmarB97

@OmarB97 OmarB97 commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Why

The macOS in-app updater can quit Hermes.app and never reinstall/reopen, which matches the reported issue where "Update now" does not actually update.

What changed

  • apps/desktop/electron/main.cjs: tightened the macOS bundle swap+relaunch sequence so ditto and destination replace failures are detected immediately, cleanup runs reliably, and the updater falls back to opening the rebuilt bundle directly instead of leaving the user with a dead quit.
  • hermes_state.py: set PRAGMA busy_timeout = 5000 for state.db so concurrent state.db writers no longer hit immediate database is locked failures.

How to review

Review applyUpdatesPosixInApp in apps/desktop/electron/main.cjs and the SQLite initialization block near PRAGMA foreign_keys=ON in hermes_state.py.

Evidence

Failure mode: in-app macOS update quits, no relaunch succeeds, retry does not install. With the patch, a swap failure opens the rebuilt .app directly; a successful swap continues to replace /Applications/Hermes.app as before.

Verification

  • Trigger the macOS in-app update path.
  • Confirm that failed bundle swaps open the rebuilt release/mac-arm64/Hermes.app fallback.
  • Confirm TUI/gateway/concurrent Hermes processes no longer surface state.db unavailable: database is locked under load.

Risks & gaps

  • Falls back to launching the rebuilt bundle if /Applications/Hermes.app swap fails; that may not match the user’s preferred installed copy location, but it avoids a no-app state. Shields online behavior from component changes?
    Shielding against downstream update regressions means the fix should be validated on macOS with concurrent Hermes processes (gateway, dashboard, TUI), plus a forced swap failure to exercise the fallback path before wide rollout. No full automated reproducer is included here.

@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have labels Jun 3, 2026
@OmarB97

OmarB97 commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

Closing: this branch's only commit is a strict subset of #38446 (codesign re-seal minus the chmod), which was just rebased onto current main and carries it all. The main.cjs swap-fail-fast + hermes_state.py busy_timeout changes this PR's description promises are not on the branch (apparently lost in an old force-push) — that work is tracked separately for recovery.

@OmarB97 OmarB97 closed this Jun 9, 2026
OmarB97 pushed a commit to OmarB97/hermes-agent that referenced this pull request Jul 11, 2026
…erable

The macOS in-app updater's detached swap script ran with plain 'set -u'
and a success-gated ditto: when ditto or the destination move failed,
the script fell through silently -- the app had already quit, the old
bundle stayed (or was left moved aside), and 'open "$DST"' either
relaunched the stale build or nothing at all. This matches the reported
"Update now does not actually update" behavior.

Make the swap fail-fast and recoverable:
- set -euo pipefail so unexpected failures stop the script instead of
  compounding.
- Detect ditto and destination-replace failures explicitly; on either,
  fall back to opening the freshly rebuilt bundle directly so the user
  is never left with a dead quit.
- Clean up the .hermes-update-old copy via an EXIT trap so it is
  removed on every exit path.

Recovered from the pre-force-push head of desktop-mac-swap-fix
(NousResearch#38410, which lost this work to a force-push
and was closed). The sqlite busy_timeout half of that PR is
intentionally not revived: the BEGIN IMMEDIATE + jitter-retry redesign
(NousResearch#3385) deliberately keeps the connection busy handler short, and a 5s
busy_timeout would override that design.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P3 Low — cosmetic, nice to have type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants