Repository navigation
[Bug][Windows] Transactional ocx update staging fails ENOTDIR mkdir on unusable npm cache root (dangling %LOCALAPPDATA%\npm-cache junction); cache preflight is windows_skip #6288
Description
Client or integration
OpenCodex dashboard
Area
Installation or packaging
Summary
On Windows, the transactional update (ocx update / dashboard worker) fails during staging npm install with:
update command failed (1). Run 'ocx update' in a terminal to see the reason and the exact recovery step.
Terminal ocx update then prints the real failure:
Updating to vX.Y.Z (transactional)...
Staging @bitkyc08/opencodex@X.Y.Z into .ocx-staging-...
npm error code ENOTDIR
npm error syscall mkdir
npm error path %LOCALAPPDATA%\npm-cache
npm error errno ENOTDIR
npm error Invalid response body while trying to fetch https://registry.npmmirror.com/@bitkyc08%2fopencodex:
ENOTDIR: not a directory, mkdir '%LOCALAPPDATA%\npm-cache'
npm error Log files were not written due to an error writing to the directory:
%LOCALAPPDATA%\npm-cache\_logs
opencodex: update aborted before touching the live install (stage): npm staging install failed (1)
Observed root cause on one machine (environment, not necessarily unique):
%LOCALAPPDATA%\npm-cache was a directory junction whose target directory did not exist (dangling reparse point). npm's mkdir of the cache root then returns ENOTDIR. Recreating the junction target made the same ocx update succeed immediately.
Why this is not only "one broken folder":
- Windows users commonly relocate
%LOCALAPPDATA%caches to another volume via junction/mount (disk-space tools, corporate profiles, manual redirects). Cleanup or drive-letter changes can leave the junction dangling while the reparse point remains. npm-cache-preflight.mjsskips entirely on win32 (windows_skip), so this class of cache-path failure is never checked before the update stops the proxy.- Staging install is
npm install -g --prefix <stage> --allow-scripts=bun ...without an explicit--cache. npm then uses its default cache resolution (%LOCALAPPDATA%\npm-cacheon Windows) even when the operator's npm config points at another cache (e.g.cache=D:/Cache/npmvia global npmrc). The GUI job log still withholds the npmpathfield, so the only actionable breadcrumb is "run ocx update in a terminal".
Related but distinct:
- [Bug][Windows] ocx update fails with ENOTDIR/errno mkdir during npm install -g; leftover .opencodex-* temp dir with locked bunx.exe #5624 — earlier ENOTDIR during legacy
npm install -g; fix: keep fenced proxies restartable and failed Windows updates recoverable (#5496, #5624) #5643 added transactional staging + recovery text. This report is the staging-phase cache mkdir failure that remains after that work. - [Bug]: Windows junction-backed CODEX_HOME prevents Codex accounts from loading #5864 — junction-backed
CODEX_HOME; same Windows junction fragility, different path and subsystem. - [Bug][Windows] Auto-update leaves proxy dead after npm EPERM on bun binary replacement #281 / [Bug][Windows] Failed ocx update leaves a file-less package skeleton; service wrapper restart-loops 1,009 times with no integrity check or backoff #1849 — bun binary locks / empty skeleton; different failure phase.
No existing issue mentions a dangling %LOCALAPPDATA%\npm-cache junction or staging cache mkdir ENOTDIR.
Reproduction
A. Dangling cache junction (environment)
- Windows, opencodex installed via npm global, service-managed proxy running.
- Make
%LOCALAPPDATA%\npm-cachea directory junction to another volume (e.g.E:\AppDataLocal\npm-cache). - Delete or rename the junction target so the reparse point remains but
Test-Pathon the target is false. - Run
ocx update(dashboard or terminal). - Staging
npm installfails:ENOTDIR/mkdirof%LOCALAPPDATA%\npm-cache. Live package is preserved; service is restored. - Recreate the target directory; re-run
ocx update→ succeeds.
B. Behavior worth fixing even without a broken junction
- Configure npm
cacheto a non-default location (global or user npmrc). - Trigger transactional
ocx updateand observe which cache path npm actually uses for staging (npm debug:logfile ... dir:/ errorpath). - On this machine, staging resolved
%LOCALAPPDATA%\npm-cache, not the configured cache.
C. Diagnostic opacity
- Fail an update from the dashboard worker only.
- Job record shows
update command failed (1)and withholds npmpath. - The exact failing path only appears if the operator re-runs
ocx updatein a terminal.
Version
Failure: 2.72.0 → 2.73.0 (transactional staging path present).
Also observed earlier on pre-#5643 updaters with the same ENOTDIR / mkdir signature (2.59.0 → 2.63.0, 2.63.0 → 2.72.0) where the GUI log also withheld the path.
Operating system
Windows 11 (build 10.0.26100), AMD64. npm 11.8.0, Node v24.x. Installer: npm global. Registry: npmmirror (metadata only; integrity check passed before staging).
Provider and model
Not provider-specific. Fails in update staging before any provider traffic.
Logs or error output
# Terminal `ocx update` (the useful log; GUI job keeps the same fields path-redacted)
npm error code ENOTDIR
npm error syscall mkdir
npm error path %LOCALAPPDATA%\npm-cache
npm error errno ENOTDIR
npm error Invalid response body while trying to fetch <registry>/@bitkyc08%2fopencodex:
ENOTDIR: not a directory, mkdir '%LOCALAPPDATA%\npm-cache'
npm error Log files were not written due to an error writing to the directory:
%LOCALAPPDATA%\npm-cache\_logs
opencodex: update aborted before touching the live install (stage): npm staging install failed (1)
opencodex: update failed after stopping the proxy — restoring the previous background service.
Job sanitizer output (dashboard):
"error": "update command failed (1). Run 'ocx update' in a terminal to see the reason and the exact recovery step."Filesystem probe that identified the environment fault (names only; no user profile):
%LOCALAPPDATA%\npm-cache → Junction → <other-volume>\npm-cache
<other-volume>\npm-cache → missing (Test-Path false)
mkdir via npm → ENOTDIR
recreate target dir → ocx update succeeds
Screenshots and supporting files
None. Full npm verbose log of the successful retry was local-only and not retained.
Redacted configuration
{
"installer": "npm",
"staging": "npm install -g --prefix .ocx-staging-* --allow-scripts=bun --no-audit --no-fund",
"npm_cache_configured": "<user-or-global-npmrc-cache>",
"npm_cache_used_by_staging_error_path": "%LOCALAPPDATA%/npm-cache",
"cache_path_shape": "dangling directory junction (target missing)",
"note": "No tokens, emails, or absolute user-profile paths included."
}Is this only on one machine?
| Layer | Scope |
|---|---|
Dangling %LOCALAPPDATA%\npm-cache junction |
This machine's environment (created by an AppData redirect; target later disappeared) |
Staging npm install failing with ENOTDIR mkdir on an unusable cache root |
General — any unusable default cache path (dangling junction, file-in-the-way, ACL, missing drive) |
Cache preflight windows_skip |
General — Windows never preflights cache writability before stopping the proxy |
Staging not pinning/--cache to the operator's configured cache |
General (needs maintainer confirmation of intended npm config inheritance) |
Job log withholding npm path |
General — diagnostic gap; 2.72+ only adds "run in terminal" |
Suggested fix
- Pin or honor npm cache for staging. Prefer passing the same cache npm reports (
npm config get cache) as--cache(ornpm_config_cache) for the staging install, so staging matches interactive npm and does not surprise the operator with the Windows default path. - Extend cache preflight to Windows (or a cheap
mkdirprobe of the resolved cache root) before stopping the proxy. A dangling junction / non-directory cache root should abort with a clear message and leave the service running. - Classify
ENOTDIR/EPERM/ENOSPCon cachemkdiras a cache-path problem in the failure guidance (name%LOCALAPPDATA%\npm-cacheor the resolved cache), not a generic "npm staging install failed". - Keep a local, non-redacted npm stderr excerpt (or at least surface
path/code/syscallin the job log) so dashboard-only operators can diagnose without a second terminal run. - Optional: if the cache root is a reparse point whose target is missing, detect that explicitly and say "recreate target or fix junction" instead of raw
ENOTDIR.
Checks
- I searched existing issues and documentation.
- I removed secrets, tokens, account details, request credentials, and personal data.
Activity
Ingwannu commentedon Oct 1, 2026
Confirmed the current dev preflight still returns windows_skip before resolving/checking the cache. The report establishes the dangling junction on your machine; it does not yet establish that staging universally ignores npmrc. A fix needs to resolve cache configuration under the same environment/invocation used by staging, check that resolved root before stopping the service, and retain transactional restore on a later failure. Windows fixtures should cover a usable junction, dangling junction, file-in-the-way, and custom configured cache without modifying a real user's cache. Dashboard guidance should expose fixed classifications such as ENOTDIR/mkdir/cache-root, not raw npm stderr, full profile paths, or registry credentials. Keeping this open as a preflight/diagnostic gap, not marking it fixed by recreating one junction.
github-actions commentedon Oct 1, 2026
Automated translation bookkeeping — detected language: English.
fix(update): check and pin the npm cache root before staging on every…
fix(update): check and pin the npm cache root before staging (#6288) (#…
lidge-jun commentedon Oct 1, 2026
Fixed on dev by #6395 (09cd45d). It ships in the next release.