Stage the updater binary under a name Windows will launch #116

Merged
kleb merged 1 commit from fix/windows-updater-installer-detection into main 2026-08-26 01:30:05 +02:00
Collaborator

Summary

Every agent CLI update on Windows failed at the final step:

EACCES: permission denied, uv_spawn '.planner-update-<nonce>.exe'

The download and SHA-256 verification both succeeded; only the spawn of the detached finalizer failed, so the update never applied and the staged binary was cleaned up, leaving nothing to diagnose.

Cause

Windows installer detection. An executable without an embedded manifest whose name looks like an installer is treated as requiring elevation, so CreateProcess refuses to launch it from an ordinary process. The staged binary is spawned by exactly such a process, so the word "update" in its own filename was enough to block it.

Measured on Windows 11, spawning copies of one identical binary that differ only in name:

name spawn
probe-alpha.exe OK
.probe-alpha.exe OK
probe-update.exe EACCES
probe-updater.exe EACCES
probe-setup.exe EACCES
probe-install.exe EACCES

Ruled out along the way: no process held the file, the directory grants FullControl, a copied binary runs from that same directory, the leading dot is irrelevant, and it is not a scan race (the same file still failed eight seconds after it was written). It failed identically with and without detached/stdio: 'ignore'.

Fix

Stage as .planner-staged-<nonce> instead. Verified on the same host: .planner-staged-<nonce>.exe and planner-staged-<nonce>.exe both spawn, .planner-update-<nonce>.exe still fails.

Cleanup sweeps both stems, so a leftover staged binary from a build predating this still gets removed.

A regression test pins the staged name against the installer keywords rather than against one specific string, since the constraint is the class of names, not this one.

Verification

Full typecheck, type-aware oxlint, knip, oxfmt, and 952 bun tests pass. The two updater test files pass, including the new guard and the extended cleanup coverage.

Not addressed here: embedding an asInvoker manifest in the compiled binary would remove the whole class of problem, but that is a build-level change to the Bun compile step and worth doing separately.

## Summary Every agent CLI update on Windows failed at the final step: ``` EACCES: permission denied, uv_spawn '.planner-update-<nonce>.exe' ``` The download and SHA-256 verification both succeeded; only the spawn of the detached finalizer failed, so the update never applied and the staged binary was cleaned up, leaving nothing to diagnose. ## Cause Windows installer detection. An executable without an embedded manifest whose name looks like an installer is treated as requiring elevation, so `CreateProcess` refuses to launch it from an ordinary process. The staged binary is spawned by exactly such a process, so the word "update" in its own filename was enough to block it. Measured on Windows 11, spawning copies of one identical binary that differ only in name: | name | spawn | |---|---| | `probe-alpha.exe` | OK | | `.probe-alpha.exe` | OK | | `probe-update.exe` | EACCES | | `probe-updater.exe` | EACCES | | `probe-setup.exe` | EACCES | | `probe-install.exe` | EACCES | Ruled out along the way: no process held the file, the directory grants FullControl, a copied binary runs from that same directory, the leading dot is irrelevant, and it is not a scan race (the same file still failed eight seconds after it was written). It failed identically with and without `detached`/`stdio: 'ignore'`. ## Fix Stage as `.planner-staged-<nonce>` instead. Verified on the same host: `.planner-staged-<nonce>.exe` and `planner-staged-<nonce>.exe` both spawn, `.planner-update-<nonce>.exe` still fails. Cleanup sweeps both stems, so a leftover staged binary from a build predating this still gets removed. A regression test pins the staged name against the installer keywords rather than against one specific string, since the constraint is the class of names, not this one. ## Verification Full typecheck, type-aware oxlint, knip, oxfmt, and 952 bun tests pass. The two updater test files pass, including the new guard and the extended cleanup coverage. Not addressed here: embedding an `asInvoker` manifest in the compiled binary would remove the whole class of problem, but that is a build-level change to the Bun compile step and worth doing separately.
Every update on Windows failed at the last step:

  EACCES: permission denied, uv_spawn '.planner-update-<nonce>.exe'

Windows installer detection is the cause. An executable without an
embedded manifest whose name looks like an installer is treated as
requiring elevation, so CreateProcess refuses to launch it from an
ordinary process. The staged binary is spawned by exactly such a
process, so the word "update" in its own name was enough to strand the
update that had already downloaded and verified successfully.

Measured on Windows 11, spawning copies of one binary that differ only
in name: probe-alpha.exe and .probe-alpha.exe launch, while
probe-update.exe, probe-updater.exe, probe-setup.exe and
probe-install.exe all fail with EACCES. The leading dot is irrelevant,
and so is timing: the same file still failed eight seconds after it was
written.

Staging now uses .planner-staged-<nonce>, which launches. Cleanup sweeps
both stems so a leftover from a build that predates this still gets
removed.
Collaborator

kReview review

Verdict: no findings

No findings to address in the reviewed diff.

No concrete regressions or actionable defects are established by the changed code and visible repository context.

Reviewed by kReview at 2245e9dd69. This comment is conservative and based only on the PR diff, metadata, and supplied repository context.

Est. cost ~$0.23 (108.7k in / 560 out / gpt-5.6-sol).

<!-- codex-forgejo-review --> <!-- codex-forgejo-review-head:2245e9dd695f8c6cb4b497697770ca19995d7ed6 --> ## kReview review **Verdict:** no findings No findings to address in the reviewed diff. No concrete regressions or actionable defects are established by the changed code and visible repository context. _Reviewed by kReview at `2245e9dd69`. This comment is conservative and based only on the PR diff, metadata, and supplied repository context._ _Est. cost ~$0.23 (108.7k in / 560 out / gpt-5.6-sol)._ <!-- codex-forgejo-review-state:eyJoZWFkU2hhIjoiMjI0NWU5ZGQ2OTVmOGM2Y2I0YjQ5NzY5Nzc3MGNhMTk5OTVkN2VkNiIsInN1bW1hcnkiOiJObyBjb25jcmV0ZSByZWdyZXNzaW9ucyBvciBhY3Rpb25hYmxlIGRlZmVjdHMgYXJlIGVzdGFibGlzaGVkIGJ5IHRoZSBjaGFuZ2VkIGNvZGUgYW5kIHZpc2libGUgcmVwb3NpdG9yeSBjb250ZXh0LiIsImZpbmRpbmdzIjpbXSwiY3VtdWxhdGl2ZUNvc3QiOnsidXNkIjowLjIzMzU3MywiaW5wdXRUb2tlbnMiOjEwODY3Mywib3V0cHV0VG9rZW5zIjo1NjAsIm1vZGVsIjoiZ3B0LTUuNi1zb2wifX0= -->
kleb merged commit dd1bf90c95 into main 2026-08-26 01:30:05 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
knopersikcuo/Planner!116
No description provided.