Skip to content

[Bug][Windows] ocx update is not transactional: a failed update deletes the working install with no rollback #1942

Description

@lidge-jun

Client or integration

CLI (ocx update), npm global install

Area

Platform (Windows) / CLI

Summary

Split out of #1849, which reported two distinct defects that need different fixes and
different closure conditions. The service-restart half shipped; this is the root cause,
and it is still open.

Half Defect Status
A Service wrapper restart-loops against a missing install (1,009 × child exited with code 3) Fixed by #1877, released in v2.24.0
B ocx update can destroy a working install and leave an unbootable skeleton this issue

#1877 stopped the symptom: the wrapper no longer hammers a package that is not there.
It does nothing about how the package came to be missing.

Current behaviour

The reporter's failed update left the global package directory with 107 directories and
zero files
— no package.json, no bin/ocx.mjs, no bundled bun.exe, and every
launcher (ocx, ocx.cmd, ocx.ps1, and the opencodex siblings) removed from
%APPDATA%\npm. npm created the tree, unpacked no content, and did not roll back. The
documented recovery commands were themselves unreachable, because the launchers that would
run them were the files that had been deleted.

The update path delegates to npm install -g, which is destructive-in-place: it removes
the existing install before the replacement is verified. When it fails midway there is no
prior state left to fall back to.

Verification does exist and is not the gap — checkUpdatePackageIntegrity fails closed
before the proxy is stopped, isRealBunBinary rejects the postinstall stub, and
awaitRestartedProxyHealthy runs an identity-checked /healthz probe with a version match
and a stability window. The problem is narrower and worse than missing verification: it is
detect-only, and it runs after the old install is already gone, so a failed probe has
nothing left to restore.

Expected behaviour

An update that fails leaves the previous working install in place. The user should never
reach a state where the tool cannot repair itself.

Suggested implementation notes

A transactional shape, where the existing install is only released after the replacement
is proven:

download to staging
  → verify executable/assets/version in staging
  → snapshot the existing install
  → atomic directory or launcher switch
  → health probe the new install
  → success: drop the snapshot   |   failure: restore the snapshot

The ordering constraint is the whole point: never delete the existing install before the
replacement is verified.
"download → delete → copy" cannot be made safe by adding retries
to it.

Worth deciding explicitly, since it shapes the fix: whether to keep delegating to
npm install -g (and stage around it, e.g. install to a temp prefix and switch), or to own
the download and unpack directly. The former is less invasive; the latter is the only way to
get a truly atomic switch on Windows, where an in-use file cannot always be replaced.

Verification this needs

Fault injection at every boundary, each asserting a launchable install survives:

  • after download, before verification
  • after verification, before the snapshot
  • mid-switch (the Windows case where a partially replaced directory is the failure mode)
  • after the switch, before the health probe
  • health probe fails → snapshot restored and the old version still runs

tests/update-transaction.test.ts does not exist yet and would be new.

Additional context

Original report, environment details and the full 1,009-restart evidence are in #1849.
That issue stays open for this defect; #1877 is linked there as the mitigation half.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingcliCLI, config inject, packaging flagsinstallInstallation or packagingplatformOS/service/tray/ACL (Windows-heavy, not Windows-only)

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions