Skip to content

fix(linux-desktop): fix Electron download cache corruption handling on build - #37545

Closed
0xharryriddle wants to merge 1 commit into
NousResearch:mainfrom
0xharryriddle:fix/desktop-electron-builder-stale-unpacked
Closed

fix(linux-desktop): fix Electron download cache corruption handling on build#37545
0xharryriddle wants to merge 1 commit into
NousResearch:mainfrom
0xharryriddle:fix/desktop-electron-builder-stale-unpacked

Conversation

@0xharryriddle

Copy link
Copy Markdown
Contributor

What does this PR do?

Fixes hermes desktop failing to build/launch on Linux (and any platform) when the cached Electron download is corrupt.

Running hermes desktop on Ubuntu 24.04 dies during packaging with:

⨯ ENOENT: no such file or directory, rename
  '.../apps/desktop/release/linux-unpacked/electron' ->
  '.../apps/desktop/release/linux-unpacked/Hermes'
✗ Desktop GUI build failed

The message looks like a stale-output or permissions problem, but the real root
cause is a corrupt cached Electron zip in the per-user download cache
(~/.cache/electron/electron-v40.9.3-linux-x64.zip). unzip -t on it fails
("86257938 extra bytes … CORRUPT"). electron-builder's app-builder unpack-electron extracts the distribution from that cached zip (not from
node_modules/electron); because the zip is malformed, it writes the small
members (LICENSE, .pak, chrome-sandbox) but never the 193 MB electron
binary, then dies renaming the missing binary to Hermes. Re-running repeats
the same broken extraction forever, so the desktop app is permanently
unlaunchable until the user manually deletes a cache file they have no way to
know about.

This is the right approach because it makes hermes desktop self-heal: validate
the cache, purge corrupt zips, retry once (electron-builder re-downloads a clean
copy). It is narrow, reversible, and adds no behavior change to a working build.
A complementary beforePack cleanup hook makes packaging idempotent across
interrupted runs. Verified empirically: purging the corrupt cache + re-running
produces a working Hermes binary; cleaning only the output dir did not.

Related Issue

Fixes #37544

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✅ Tests (adding or improving test coverage)

Changes Made

  • hermes_cli/main.py:
    • Add _electron_download_cache_dirs() — resolve per-OS Electron download
      cache dirs, honoring electron_config_cache / ELECTRON_CACHE overrides.
    • Add _purge_corrupt_electron_cache() — validate every electron-*.zip
      via zipfile.testzip() (full CRC check) and delete corrupt ones;
      best-effort, never raises.
    • Wire corrupt-cache purge + single retry into cmd_gui packaged-build
      failure path (skips for --source mode).
  • apps/desktop/scripts/before-pack.cjs (new) — electron-builder beforePack
    hook that wipes the target unpacked dir before staging, so an interrupted
    prior pack can't poison the next run's rename. Cross-platform, best-effort.
  • apps/desktop/package.json — register "beforePack": "scripts/before-pack.cjs".
  • apps/desktop/scripts/before-pack.test.cjs (new) — node --test coverage for
    the cleanup helper (removes populated dir, no-ops when absent, ignores invalid
    input, hook never rejects).
  • tests/hermes_cli/test_gui_command.py — add tests: corrupt-zip detector
    removes only bad zips / no-ops when valid; cmd_gui purge→retry→launch path;
    no-retry-when-clean fail-fast path.

How to Test

  1. Reproduce the failure (simulate a corrupt cache):
    # with a corrupt zip present in ~/.cache/electron/, hermes desktop fails:
    cd apps/desktop && npx electron-builder --dir
    # ⨯ ENOENT ... rename '.../linux-unpacked/electron' -> '.../linux-unpacked/Hermes'
  2. Apply this PR and run the supported entry point:
    hermes desktop
    Expected: on the first pack failure you see
    ⚠ Detected corrupt cached Electron download (N file(s)); removed and retrying build...,
    the build re-downloads a clean Electron, packages successfully, and launches.
  3. Confirm the packaged binary exists:
    ls -la apps/desktop/release/linux-unpacked/Hermes   # 193 MB binary present
  4. Run the unit tests:
    scripts/run_tests.sh tests/hermes_cli/test_gui_command.py
    cd apps/desktop && node --test scripts/before-pack.test.cjs
    cd apps/desktop && npm run test:desktop:platforms

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q (via scripts/run_tests.sh tests/hermes_cli/test_gui_command.py) and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: Ubuntu 24.04, Node 24.12, npm 11.6, Python 3 (build verified end-to-end)

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A (behavior is internal build robustness; helper docstrings document root cause)
  • I've updated cli-config.yaml.example if I added/changed config keys — N/A (no config keys added)
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — N/A (no architecture/workflow change)
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — cache dirs resolved per-OS; beforePack platform-agnostic
  • I've updated tool descriptions/schemas if I changed tool behavior — N/A

For New Skills

Not applicable — this is a bug fix to the desktop build pipeline.

Screenshots / Logs

Failure (before):

• packaging       platform=linux arch=x64 electron=40.9.3 appOutDir=release/linux-unpacked
⨯ ENOENT: no such file or directory, rename
  '.../release/linux-unpacked/electron' -> '.../release/linux-unpacked/Hermes'

Cache corruption proof:

$ unzip -t ~/.cache/electron/electron-v40.9.3-linux-x64.zip
warning: 86257938 extra bytes at beginning or within zipfile
zip integrity test: CORRUPT (exit 4)

Recovery (after):

→ Building desktop packaged app...
  ⚠ Detected corrupt cached Electron download (1 file(s)); removed and retrying build...
• downloading     url=…/electron-v40.9.3-linux-x64.zip size=115 MB parts=8
• downloaded      duration=12.33s
$ ls -la apps/desktop/release/linux-unpacked/Hermes
-rwxr-xr-x 193.5M  Hermes

hermes desktop failed on Linux with an ENOENT renaming
release/linux-unpacked/electron -> Hermes. Root cause is a corrupt
cached Electron zip (~/.cache/electron/electron-*.zip): app-builder
unpack-electron extracts a partial tree from the bad zip that is
missing the electron binary, so electron-builder dies on the final
rename. Re-running repeats the broken extraction, leaving the desktop
app permanently unlaunchable until the cache is manually purged.

- Add _electron_download_cache_dirs() + _purge_corrupt_electron_cache()
  to hermes_cli/main.py: validate every electron-*.zip via
  zipfile.testzip() and delete corrupt ones; honor electron_config_cache
  / ELECTRON_CACHE overrides with per-OS defaults.
- Wire purge + single retry into cmd_gui packaged-build failure path so
  a poisoned download self-heals (electron re-downloads clean).
- Add beforePack hook (apps/desktop/scripts/before-pack.cjs) to wipe the
  target unpacked dir before staging, making packaging idempotent across
  interrupted runs. Cross-platform, best-effort.
- Tests: corrupt-zip detector, cmd_gui purge/retry/launch path,
  no-retry-when-clean path, and node --test for the cleanup helper.
@0xharryriddle
0xharryriddle requested a review from a team June 2, 2026 17:54
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/cli CLI entry point, hermes_cli/, setup wizard labels Jun 2, 2026

@austinpickett austinpickett left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approve. Verified against main: apps/desktop/scripts/before-pack.cjs does not exist, package.json has no beforePack hook, and _purge_corrupt_electron_cache/_electron_download_cache_dirs are absent from hermes_cli/main.py. The ENOENT electron->Hermes rename failure on an interrupted/corrupt pack is real and currently unrecoverable without a manual rm -rf.

Two-layer fix is well chosen and independent:

  1. beforePack wipes the stale appOutDir so packaging is idempotent across interrupted runs (best-effort, never throws → never masks the real build).
  2. CLI-side purge of CRC-failing electron-*.zip cache entries with a single retry, honoring ELECTRON_CACHE/electron_config_cache/XDG/platform defaults.

Cross-platform cache resolution is correct, testzip() is the right validation, and the tests cover the happy path, no-op, bad-input, retry-once, and clean-cache-no-retry cases. No notes.

@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the detailed reproduction and recovery path. This has already been implemented on main and shipped in v2026.6.5.

  • Automated hermes-sweeper review verified that the PR was salvaged as f583c6ebd5bd9a15cbb9c8a757882dd7afc8df19, preserving Harry Riddle as commit author.
  • The follow-up fef04a197e24ba607cfd03f1c3d33858a4b2e5c9 strengthened the recovery: hermes_cli/main.py:5204 clears cached Electron archives and stale unpacked output, while hermes_cli/main.py:5742 retries only for the missing-executable failure shape.
  • The packaging cleanup remains active through apps/desktop/package.json:178 and apps/desktop/scripts/before-pack.mjs:65.
  • Current regression coverage includes the prepended-junk cache case and stale output cleanup in tests/hermes_cli/test_gui_command.py:462.

The original zipfile.testzip() gate did not detect the reported prepended/concatenated-junk corruption, so the follow-up intentionally replaced it with the current cache-refresh flow.

@teknium1 teknium1 closed this Jul 13, 2026
@teknium1 teknium1 added the sweeper:implemented-on-main Sweeper: behavior already present on current main label Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists sweeper:implemented-on-main Sweeper: behavior already present on current main type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: hermes desktop build fails on Linux with ENOENT: rename 'electron' -> 'Hermes' when the cached Electron download is corrupt

4 participants