Skip to content

fix(desktop): exclude respawned backends from venv-blocker scan (#77277) - #77772

Closed
RelaxJonh wants to merge 1 commit into
NousResearch:mainfrom
RelaxJonh:fix/desktop-update-respawn-loop-77277
Closed

fix(desktop): exclude respawned backends from venv-blocker scan (#77277)#77772
RelaxJonh wants to merge 1 commit into
NousResearch:mainfrom
RelaxJonh:fix/desktop-update-respawn-loop-77277

Conversation

@RelaxJonh

Copy link
Copy Markdown
Contributor

Fixes #77277

Root Cause

The Desktop in-app updater on Windows enters an infinite loop: releaseBackendLockForUpdate kills the backend, but the Desktop app respawns it within seconds, and scanVenvBlockers reports the respawned process as a blocker. The user sees "Update aborted: another Hermes process is using this installation" with a different PID each retry.

The waitForUpdateClearance gate (#73822) prevents startHermes/spawnPoolBackend from respawning during the update, but on older versions (v0.19.0) the gate may not be present or may have race conditions. Even with the gate, the scanner itself has no way to distinguish the Desktop's own managed processes from external holders.

Fix

Add --exclude-children-of <PID> to _scan_venv_blockers.py. When the Desktop app invokes the scanner, it passes its own PID (process.pid), causing the scanner to exclude all descendants of the Electron main process via a psutil process tree walk. This covers both the original backend PIDs and any respawned ones.

Changes

File Change
hermes_cli/_scan_venv_blockers.py Add argparse --exclude-children-of arg + _collect_descendant_pids() helper
apps/desktop/electron/venv-blocker-scan.ts Add ScanOptions interface, pass --exclude-children-of to Python subprocess
apps/desktop/electron/main.ts Pass process.pid as excludeChildrenOf in applyUpdates preflight
tests/hermes_cli/test_scan_venv_blockers.py Update test helper lambda to accept kwargs

Testing

  • All 23 existing test_scan_venv_blockers.py tests pass
  • _collect_descendant_pids uses psutil process tree walk — safe on non-Windows (returns empty set)
  • parse_known_args ensures backward compatibility with CLI usage (no args = old behavior)

@alt-glitch alt-glitch added type/bug Something isn't working comp/desktop Electron desktop app (apps/desktop/*) comp/cli CLI entry point, hermes_cli/, setup wizard platform/windows Native Windows-specific behavior or breakage P2 Medium — degraded but workaround exists sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows labels Aug 3, 2026
…Research#77277)

The Desktop in-app updater on Windows enters an infinite loop when
updating: releaseBackendLockForUpdate kills the backend, but the
Desktop app respawns it within seconds, and scanVenvBlockers reports
the respawned process as a blocker. The user sees "Update aborted:
another Hermes process is using this installation" with a different
PID each time they retry.

Root cause: scanVenvBlockers calls _detect_venv_python_processes()
without excluding the Desktop app's own managed processes. After
releaseBackendLockForUpdate kills the backend, the Desktop app's
ensureBackend/startHermes respawns it, and the scanner picks it up
as an external blocker.

Fix: Add --exclude-children-of <PID> to _scan_venv_blockers.py.
When the Desktop app invokes the scanner, it passes its own PID
(process.pid), causing the scanner to exclude all descendants of
the Electron main process. This covers both the original backend
PIDs and any respawned ones.

Changes:
- hermes_cli/_scan_venv_blockers.py: Add argparse --exclude-children-of
  and _collect_descendant_pids() helper using psutil process tree walk.
- apps/desktop/electron/venv-blocker-scan.ts: Add ScanOptions interface
  with excludeChildrenOf param, pass --exclude-children-of to subprocess.
- apps/desktop/electron/main.ts: Pass process.pid as excludeChildrenOf
  to scanVenvBlockers in the applyUpdates preflight.
- tests/hermes_cli/test_scan_venv_blockers.py: Update test helper to
  accept kwargs from _detect_venv_python_processes.
@teknium1

Copy link
Copy Markdown
Contributor

Closing after a premise-check against current main — the diagnosis was real and useful, but both halves have been overtaken:

  1. The respawn loop is closed upstream of the scanner now. waitForUpdateClearance gates every backend start while an update is in flight, and releaseBackendLock re-collects and re-kills respawned stragglers in a 15s loop BEFORE the blocker scan runs — the scanner no longer sees the Desktop's own respawns on current main. (As your PR body noted, the loop lived on builds where the gate wasn't present — but a fix merged today can't ship to v0.19.0; those installs heal by updating.)
  2. Blanket descendant-exclusion is fail-open. Excluding everything under the Electron PID would also exempt a pty terminal running hermes inside Desktop, or a Desktop-spawned gateway — the update would then proceed into a genuinely held venv, recreating the half-updated-install class we just fail-closed (fix(update): a contended Windows venv is never mutated — failed shim quarantine refuses instead of warning (#87331) #92617). The field reports on Desktop in-app update loops forever on Windows: updater reports its own respawning backend as the venv blocker #77277 (Scheduled-Task and Startup-VBS launchers) reached the same conclusion about ancestry-based exclusion from the other direction.

The durable replacement for "whose process is this?" is identity, not ancestry: the spawn ledger (self-registered pid+create_time+purpose+spawner, landed via #91869/#92698) and, next, the gateway control socket's pause-for-update verb (#92091), which turns this whole scan-and-guess preflight into a negotiated drain. Thanks for the clear repro table in the PR — it's what made the premise-check quick.

@teknium1 teknium1 closed this Aug 23, 2026
@teknium1

Copy link
Copy Markdown
Contributor

Closing after a premise-check against current main (part of the #91277 venv-holder slice 2 pass) — this was a reasonable fix against v0.19-era code, but main has since moved in a direction that conflicts with the mechanism here:

  1. The respawn loop this targets is now handled upstream: the waitForUpdateClearance gate (Windows: Desktop self-update can never succeed — applyUpdates' own SIGTERM triggers a renderer reconnect that respawns the backend before scanVenvBlockers runs #73822) blocks startHermes/spawnPoolBackend during updates, and the teardown path re-collects and re-kills straggler backends each pass (apps/desktop/electron/main.ts, releaseBackendLock loop) instead of trusting the initial sweep.
  2. Blanket descendant exclusion (--exclude-children-of <Electron PID>) conflicts with the fail-closed contract that landed via fix(update): a contended Windows venv is never mutated — failed shim quarantine refuses instead of warning (#87331) #92617/fix(update): ZIP fallback no longer destroys user work or the desktop build (#87331, #91962, #87304, #70337 — salvage #87878 + #70477) #92013: a genuinely stuck child holding the venv would become scan-invisible and the update would proceed into exactly the half-updated-venv scenario that work eliminated. Main's approach is narrow, targeted exemption (the fix(update): Windows venv-holder guard names holders correctly and can see the gateway it must pause (#90778, #87594 — proven live on windows-latest) #91869 gateway-ancestor carve-out via the canonical looks_like_gateway_command_line matcher), not tree-wide exclusion.

#77277 stays open — the durable fix for the respawn dance is the gateway control socket's pause-for-update verb (#92091, step 1 landed in #92447). Thanks for the detailed root-cause writeup; the respawned-PID-each-retry analysis was correct and helped shape the socket design.

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 comp/desktop Electron desktop app (apps/desktop/*) P2 Medium — degraded but workaround exists platform/windows Native Windows-specific behavior or breakage sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Desktop in-app update loops forever on Windows: updater reports its own respawning backend as the venv blocker

3 participants