Die halbe Windows-Aktualisierung: hermes.exe ist weg, der Versionsstempel bleibt zurück

Du führst hermes update unter Windows aus, siehst einen Bildschirm voller Logzeilen durchlaufen, und die letzte ist:
⚠ Update follow-up 'launchers' did not finish: launchers: source launcher publication failed — retry the source update (the next launch or `hermes update` retries it)
Das liest sich wie „das erledigt sich beim nächsten Mal von selbst“, also schließt du das Fenster. Am nächsten Tag meldet hermes --version weiterhin die alte Version, bin\hermes.exe ist weg, und übrig bleibt nur hermes.cmd. Ein erneuter Lauf des Updates druckt dieselbe Warnung. Zwei PRs, gemergt am 22. und 23. Oktober 2026, zerlegen diese Schleife.
Zwei Symptome, eine Ursache
Die Logzeile sind eigentlich zwei getrennte Fehler, die übereinander liegen:
- Eine laufende exe lässt sich nicht löschen. Das Update hat Python neu gepinnt (
pm/lock.jsonvon 3.14.7 auf 3.14.9), also müssen die Launcher —bin\hermes.exeund Konsorten — neu veröffentlicht werden. Die Veröffentlichung löscht zuerst die altehermes.exe, damit sie den neuen Befehl nicht verdeckt, und Windows verweigert das Löschen eines gemappten Images. Dieses Image ist genau das, das geradehermes updateausführt. - Der Stamp-Schreibvorgang entfällt, wenn die Veröffentlichung scheitert. Kein Launcher, kein
write_source_stamp— also sageninstall-stamp.jsonund.hermes-bootstrap-completeweiterhinv0.22.4.
Dann die dritte Ebene, die das Ganze festnagelt: Der Rat in der Warnung — der nächste Start oder ein weiteres hermes update versucht es erneut — versucht es aus derselben exe heraus. Das kann nie aufgehen. Du wartest auf Selbstheilung; sie wartet auf eine andere exe.
Warum nur Windows
Unter POSIX lässt sich eine laufende Datei unlinken, und der Prozess nutzt den Inode weiter. Windows erlaubt das Löschen eines gemappten Images nicht.
Auch der Auslöser ist eng: Windows, und nur bei einem Wechsel der Python-Version. Beim Update von v0.22.3 auf v0.22.4 blieb Python bei 3.14.7, die vorhandene exe passte also und blieb. Genau das macht den Fehler schwer zu finden — die meisten Updates laufen sauber durch.
Fix eins: wenn es sich nicht löschen lässt, umbenennen und beiseitelegen
#143291 fügt _retire_exe in hermes_cli/_launchers.py hinzu: erst der Löschversuch; wenn Windows wegen einer benutzten Datei ablehnt, wird sie als hermes.exe.<pid>.deleteme beiseite umbenannt. Ein gemapptes Image darf Windows umbenennen — distlib macht genau dieselbe Bewegung. Die beiseitegelegte Kopie wird bei einer späteren Veröffentlichung eingesammelt, wenn ihr Prozess beendet ist.
Da die Veröffentlichung nicht mehr scheitert, wird der Stamp wieder geschrieben. _collect_retired räumt außerdem die hermes.exe.deleteme ab, die distlib selbst hinterlässt, wenn die exe des Updates die ersetzte ist.
Fix zwei: nicht mehr auf .cmd degradieren
Den Löschvorgang allein zu reparieren reichte nicht. #143339 dokumentiert die andere Hälfte: Der Update-Prozess aktiviert nur die Dependency-Umgebung, die kein distlib hat, und die site-packages des Store-Python (dort lebt pip) fallen weg. mint_launcher findet dann keinen ScriptMaker und veröffentlicht eine .cmd — und weil der vorige Fix die laufende exe inzwischen beiseite umbenennen kann, war diese exe endgültig in Rente. Am Ende bleibt eine Installation mit hermes.cmd und sonst nichts.
Der Installer hat seine erste exe ursprünglich über das mitgelieferte pip von python-build-standalone geprägt, einen Weg, den das Update nie befragt hat. Der Fix verbindet ihn: Wenn der laufende Prozess distlib nicht importieren kann, lädt mint_launcher pip._vendor.distlib von neben dem Interpreter, an den es bindet (<python>\Lib\site-packages\pip\_vendor\distlib, in jedem PBS-Build enthalten), unter einem privaten Modulnamen, damit das pip des Stores prozessintern nie auf import pip antwortet. Ein .cmd-Fallback bleibt für einen Interpreter ganz ohne distlib — er ist nur selten geworden.
Was du auf deiner Maschine prüfen solltest
Wenn du unter Windows bist und kürzlich einen Wechsel der Python-Version mitgemacht hast, drei schnelle Checks:
hermes --version # semantische Version und gebundenes Python
- Ist
bin\hermes.exenoch da, in deinem Hermes-Home? Nurhermes.cmdheißt, du hast genau das erwischt. - Öffne
install-stamp.jsonund.hermes-bootstrap-complete: Der Commit, den sie nennen, sollte der Build sein, den du tatsächlich ausführst, nicht der vorherige. - Wenn sie nicht übereinstimmen, komm zuerst auf einen Build mit beiden Fixes und führe dann noch einmal
hermes updateaus — nur der neue Veröffentlichungscode kann die alte exe beiseiteschieben, während sie läuft.
Wirkt die Installation wirklich kaputt statt nur halb aktualisiert (ein dreckiger Git-Worktree, Befehle, die mal existieren und mal nicht), hat der Leitfaden zur Reparatur eines beschädigten Git die längere Checkliste. Kanal- und Rollback-Semantik stehen im Stable-Channel-Leitfaden, die Flags in der hermes update-Referenz.
Release-Status
Beide Fixes wurden am 22. und 23. Oktober 2026 nach main gemergt; ein dritter PR (#143261) behebt den Plattenverbrauch der Windows-E2E, der den Runner gefüllt hatte — jede Journey löscht nun ihre Installation beim Teardown. Aufgefallen sind die Fehler im E2E-Lauf des Release Candidate v0.22.5 auf dem Upgrade-Pfad von v0.22.4 nach HEAD, betroffen sind also Windows-Nutzer, die von v0.22.4 aus aktualisieren. Das letzte Release ist weiterhin v0.22.4 (21. Oktober): Diese Fixes haben den Stable-Kanal noch nicht erreicht.
Eine Anmerkung, warum das nicht bloß CI-Hausmeisterei ist: Sobald ein Windows-Launcher auf .cmd degradiert, scheitern Verknüpfungen, geplante Aufgaben und jedes Skript, das .exe im PATH-Eintrag stehen hat, still — und das einzige Signal ist diese eine ⚠-Zeile.