Skip to content

[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

Closed

Description

@NovaTrailX

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":

  1. 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.
  2. npm-cache-preflight.mjs skips entirely on win32 (windows_skip), so this class of cache-path failure is never checked before the update stops the proxy.
  3. Staging install is npm install -g --prefix <stage> --allow-scripts=bun ... without an explicit --cache. npm then uses its default cache resolution (%LOCALAPPDATA%\npm-cache on Windows) even when the operator's npm config points at another cache (e.g. cache=D:/Cache/npm via global npmrc). The GUI job log still withholds the npm path field, so the only actionable breadcrumb is "run ocx update in a terminal".

Related but distinct:

No existing issue mentions a dangling %LOCALAPPDATA%\npm-cache junction or staging cache mkdir ENOTDIR.

Reproduction

A. Dangling cache junction (environment)

  1. Windows, opencodex installed via npm global, service-managed proxy running.
  2. Make %LOCALAPPDATA%\npm-cache a directory junction to another volume (e.g. E:\AppDataLocal\npm-cache).
  3. Delete or rename the junction target so the reparse point remains but Test-Path on the target is false.
  4. Run ocx update (dashboard or terminal).
  5. Staging npm install fails: ENOTDIR / mkdir of %LOCALAPPDATA%\npm-cache. Live package is preserved; service is restored.
  6. Recreate the target directory; re-run ocx update → succeeds.

B. Behavior worth fixing even without a broken junction

  1. Configure npm cache to a non-default location (global or user npmrc).
  2. Trigger transactional ocx update and observe which cache path npm actually uses for staging (npm debug: logfile ... dir: / error path).
  3. On this machine, staging resolved %LOCALAPPDATA%\npm-cache, not the configured cache.

C. Diagnostic opacity

  1. Fail an update from the dashboard worker only.
  2. Job record shows update command failed (1) and withholds npm path.
  3. The exact failing path only appears if the operator re-runs ocx update in 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

  1. Pin or honor npm cache for staging. Prefer passing the same cache npm reports (npm config get cache) as --cache (or npm_config_cache) for the staging install, so staging matches interactive npm and does not surprise the operator with the Windows default path.
  2. Extend cache preflight to Windows (or a cheap mkdir probe 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.
  3. Classify ENOTDIR/EPERM/ENOSPC on cache mkdir as a cache-path problem in the failure guidance (name %LOCALAPPDATA%\npm-cache or the resolved cache), not a generic "npm staging install failed".
  4. Keep a local, non-redacted npm stderr excerpt (or at least surface path/code/syscall in the job log) so dashboard-only operators can diagnose without a second terminal run.
  5. 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

  1. added
    bugSomething isn't working
    cliCLI, config inject, packaging flags
    installInstallation or packaging
    platformOS/service/tray/ACL (Windows-heavy, not Windows-only)
    on Sep 30, 2026
  2. Ingwannu commented on Oct 1, 2026

    @Ingwannu
    Owner

    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.

  3. github-actions commented on Oct 1, 2026

    @github-actions
    Contributor

    Automated translation bookkeeping — detected language: English.

  4. added 2 commits that reference this issue on Oct 1, 2026
    3cd341a
    09cd45d
  5. lidge-jun commented on Oct 1, 2026

    @lidge-jun
    Owner

    Fixed on dev by #6395 (09cd45d). It ships in the next release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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