The Windows half-update: hermes.exe gone, version stamp left behind

You run hermes update on Windows, watch a screen of log lines, and the last one is:
⚠ Update follow-up 'launchers' did not finish: launchers: source launcher publication failed — retry the source update (the next launch or `hermes update` retries it)
It reads like “this will fix itself next time”, so you close the window. The next day hermes --version still reports the old version, bin\hermes.exe is gone, and only hermes.cmd is left. Running the update again prints the same warning. Two PRs merged on October 22-23, 2026 take that loop apart.
Two symptoms, one root cause
The log line is really two separate failures stacked:
- A running exe cannot be deleted. The update repinned Python (
pm/lock.jsonmoved from 3.14.7 to 3.14.9), so the launchers —bin\hermes.exeand friends — have to be republished. Publication deletes the oldhermes.exefirst so it cannot shadow the new command, and Windows refuses to delete a mapped image. The image in question is the one currently runninghermes update. - The stamp write is skipped when publication fails. No launcher, no
write_source_stamp— soinstall-stamp.jsonand.hermes-bootstrap-completestill sayv0.22.4.
Then the third layer, which is what makes it stick: the advice in the warning — the next launch or another hermes update retries it — retries from that same exe. It can never clear. You are waiting for it to self-heal; it is waiting for a different exe.
Why Windows only
On POSIX, a running file can be unlinked and the process keeps using the inode. Windows does not allow deleting a mapped image.
The trigger is narrow too: Windows, and only across a Python version change. Updating from v0.22.3 to v0.22.4 kept Python at 3.14.7, so the existing exe matched and stayed. That is exactly what makes it hard to spot — most updates are fine.
Fix one: if it will not delete, rename it aside
#143291 adds _retire_exe in hermes_cli/_launchers.py: try the delete; when Windows refuses because the file is in use, rename it aside as hermes.exe.<pid>.deleteme. Windows does allow renaming a mapped image — distlib uses the same move. The aside copy is collected on a later publication, once its process has exited.
With publication no longer failing, the stamp gets written again. _collect_retired also sweeps the hermes.exe.deleteme that distlib itself leaves behind when the update’s own exe is the one being replaced.
Fix two: stop degrading to .cmd
Fixing the delete alone was not enough. #143339 documents the other half: the update process activates only the dependency environment, which has no distlib, and the store Python’s site-packages (where pip lives) are dropped. mint_launcher then finds no ScriptMaker and publishes a .cmd — and because the previous fix can now rename the running exe aside, that exe was retired for good. The install ends up with hermes.cmd and nothing else.
The installer originally minted its first exe through python-build-standalone’s bundled pip, which the update never consulted. The fix connects that path: when the running process cannot import distlib, mint_launcher loads pip._vendor.distlib from beside the interpreter it is binding (<python>\Lib\site-packages\pip\_vendor\distlib, shipped by every PBS build), under a private module name so the store’s pip never answers import pip in-process. A .cmd fallback remains for an interpreter with no distlib at all — it is just rare now.
What to check on your machine
If you are on Windows and recently crossed a Python version change, three quick checks:
hermes --version # semantic version and bound Python
- Is
bin\hermes.exestill there, under your Hermes home? Onlyhermes.cmdmeans you hit this. - Open
install-stamp.jsonand.hermes-bootstrap-complete: the commit they name should be the build you are actually running, not the previous one. - If they disagree, get onto a build that contains both fixes first, then run
hermes updateonce more — only the new publication code can move the old exe out of the way while it is running.
If the install looks genuinely broken rather than half-updated (a dirty git worktree, commands that sometimes exist and sometimes do not), the damaged git repair guide has the longer checklist. Channel and rollback semantics are in the stable channel guide, and the flags live in the hermes update reference.
Release status
Both fixes merged to main on October 22-23, 2026; a third PR (#143261) fixes the Windows E2E disk footprint that had filled the runner — each journey now deletes its install at teardown. The failures were caught by the v0.22.5 release-candidate E2E run on the v0.22.4 to HEAD upgrade path, so the affected population is Windows users updating from v0.22.4. The latest release is still v0.22.4 (October 21): these fixes have not reached the stable channel yet.
One note on why this is not just CI housekeeping: once a Windows launcher degrades to .cmd, shortcuts, scheduled tasks and any script with .exe in its PATH entry fail silently — and the only signal is that one ⚠ line.