Summary
On Windows 11, an ocx update from 2.13.0 left the global package directory containing 107 directories and zero files.
Every launcher (ocx, ocx.cmd, ocx.ps1, opencodex, opencodex.cmd, opencodex.ps1) was removed from
%APPDATA%\npm, so no documented recovery command was reachable from a normal shell.
The Task Scheduler service wrapper then restarted the missing entry point every 5 seconds for 89 minutes —
1,009 consecutive child exited with code 3 events — until the package was manually rolled back.
This matches the partial-install behaviour described in #281. That issue was closed with the permanent-crash symptom
attributed to an external monitoring script terminating the restarted proxy. That attribution does not fit this case:
no external process was involved. The wrapper was relaunching a package that had no bin/ocx.mjs, no
src/cli/index.ts, and no bundled bun.exe to execute.
Environment
| Item |
Value |
| OS |
Windows 11 Home, 10.0.26100 build 26100, AMD64 |
| Node |
v24.15.0 |
| npm |
11.19.0 |
| opencodex (before/after rollback) |
2.13.0 |
| Bundled Bun |
1.3.14 |
| Service backend |
Task Scheduler (opencodex-proxy), wrapper opencodex-service.cmd |
| Install type |
npm global, %APPDATA%\npm\node_modules\@bitkyc08\opencodex |
Evidence
1. The abandoned install contained directories only
The failed package was preserved before rollback. Recursive enumeration:
TotalDirs : 107
TotalFiles : 0
95 dependency folders were created under node_modules (@bufbuild, @hono,
@modelcontextprotocol, @napi-rs, @oven, bun, ...). The node_modules/bun folder
exists and is completely empty. Missing at the package root: package.json,
bin/ocx.mjs, src/cli/index.ts, node_modules/bun/bin/bun.exe.
So npm created the directory tree, then unpacked no file content and did not roll back.
2. Launchers were removed, recovery commands unreachable
%APPDATA%\npm\ocx.cmd and all sibling launchers were gone. What remained were npm's
rename-aside shims, which still pointed at the now-nonexistent target:
.ocx-cl2A1bun .opencodex-lwm6eEVg
.ocx.cmd-RmK066DM .opencodex.cmd-XPiSZ201
.ocx.ps1-AKozk9bb .opencodex.ps1-p35htcAv
A stale scope directory was also left behind (same pattern as #281).
With no ocx on PATH, the documented remedies (ocx service repair, ocx doctor,
ocx update) cannot be run without manually reconstructing the Bun + CLI invocation.
3. Unbounded restart loop with no integrity check
service.log, filtered to this incident:
[16/08/2026 12:33:51.44] child exited with code 3; restarting in 5s
... 1,009 occurrences, one every ~5 seconds ...
[16/08/2026 14:02:50.11] child exited with code 3; restarting in 5s
First failure was 45 seconds after Windows boot (boot 12:33:06), so the broken package was
already on disk before the restart, and the loop began immediately at logon and never self-limited.
For contrast, code 3 appeared only 25 times in the entire preceding log history
(31/07 x1, 02/08 x13, 08/08 x11). This was not pre-existing noise.
The wrapper loop in opencodex-service.cmd is:
:loop
"%OCX_BUN%" "%OCX_CLI%" start --port 10100 >>"%OCX_SERVICE_LOG%" 2>&1
if %ERRORLEVEL% NEQ 0 (
ping -n 6 127.0.0.1 >nul
goto loop
)
There is no check that %OCX_BUN% or %OCX_CLI% exist, no failure counter, and no backoff.
A permanently unrecoverable condition is retried identically forever.
Reproduction
- Windows, opencodex installed globally via npm and running under the Task Scheduler backend.
- Run an update that triggers a global
npm install while the service is live and Bun binaries are in use.
- npm hits a locked-file error, leaves the package as a directory skeleton, and does not roll back.
- Launchers disappear from
%APPDATA%\npm; ocx is no longer on PATH.
- The service wrapper loops indefinitely on the missing entry point.
Impact
All model routing through the proxy is down. Because the CLI is also gone, the failure is not
self-recoverable through the documented commands. Recovery here required restoring the previous
package directory and launcher shims manually, out of band.
Suggested fixes
Updater (relates to #281)
- Stage the new version and swap atomically, so a failed unpack cannot replace a working install.
- On failure, roll back to the previous package and restore launchers rather than leaving a skeleton.
- Stop the service and release Bun file handles before the install step, not after.
- Verify
package.json, bin/ocx.mjs, src/cli/index.ts and the bundled runtime exist before declaring success.
Service wrapper (separate, arguably higher value)
- Preflight the launcher and runtime paths before the first spawn; if either is missing, log one clear
"installation is incomplete, run X" line and stop rather than looping.
- Add exponential backoff and a consecutive-failure ceiling.
- Distinguish "crashed at runtime" from "cannot start at all" in the log.
A single existence check would have converted an 89-minute silent outage into one actionable log line.
Notes
- Rolled back to 2.13.0; the proxy is healthy again and has since survived a restart
(status=protected, protection=service, rebootSafe=true).
- The failed install directory has been preserved and can be inspected further if useful.
- Happy to test a patched updater on this machine.
Summary
On Windows 11, an
ocx updatefrom 2.13.0 left the global package directory containing 107 directories and zero files.Every launcher (
ocx,ocx.cmd,ocx.ps1,opencodex,opencodex.cmd,opencodex.ps1) was removed from%APPDATA%\npm, so no documented recovery command was reachable from a normal shell.The Task Scheduler service wrapper then restarted the missing entry point every 5 seconds for 89 minutes —
1,009 consecutive
child exited with code 3events — until the package was manually rolled back.This matches the partial-install behaviour described in #281. That issue was closed with the permanent-crash symptom
attributed to an external monitoring script terminating the restarted proxy. That attribution does not fit this case:
no external process was involved. The wrapper was relaunching a package that had no
bin/ocx.mjs, nosrc/cli/index.ts, and no bundledbun.exeto execute.Environment
opencodex-proxy), wrapperopencodex-service.cmd%APPDATA%\npm\node_modules\@bitkyc08\opencodexEvidence
1. The abandoned install contained directories only
The failed package was preserved before rollback. Recursive enumeration:
95 dependency folders were created under
node_modules(@bufbuild,@hono,@modelcontextprotocol,@napi-rs,@oven,bun, ...). Thenode_modules/bunfolderexists and is completely empty. Missing at the package root:
package.json,bin/ocx.mjs,src/cli/index.ts,node_modules/bun/bin/bun.exe.So npm created the directory tree, then unpacked no file content and did not roll back.
2. Launchers were removed, recovery commands unreachable
%APPDATA%\npm\ocx.cmdand all sibling launchers were gone. What remained were npm'srename-aside shims, which still pointed at the now-nonexistent target:
A stale scope directory was also left behind (same pattern as #281).
With no
ocxon PATH, the documented remedies (ocx service repair,ocx doctor,ocx update) cannot be run without manually reconstructing the Bun + CLI invocation.3. Unbounded restart loop with no integrity check
service.log, filtered to this incident:First failure was 45 seconds after Windows boot (boot 12:33:06), so the broken package was
already on disk before the restart, and the loop began immediately at logon and never self-limited.
For contrast, code 3 appeared only 25 times in the entire preceding log history
(31/07 x1, 02/08 x13, 08/08 x11). This was not pre-existing noise.
The wrapper loop in
opencodex-service.cmdis:There is no check that
%OCX_BUN%or%OCX_CLI%exist, no failure counter, and no backoff.A permanently unrecoverable condition is retried identically forever.
Reproduction
npm installwhile the service is live and Bun binaries are in use.%APPDATA%\npm;ocxis no longer on PATH.Impact
All model routing through the proxy is down. Because the CLI is also gone, the failure is not
self-recoverable through the documented commands. Recovery here required restoring the previous
package directory and launcher shims manually, out of band.
Suggested fixes
Updater (relates to #281)
package.json,bin/ocx.mjs,src/cli/index.tsand the bundled runtime exist before declaring success.Service wrapper (separate, arguably higher value)
"installation is incomplete, run X" line and stop rather than looping.
A single existence check would have converted an 89-minute silent outage into one actionable log line.
Notes
(
status=protected,protection=service,rebootSafe=true).