Thanks to visit codestin.com
Credit goes to github.com

Skip to content

aiox update fails on pnpm/yarn projects — runs npm install in project root #785

@ruibrunocunha

Description

@ruibrunocunha

aiox update fails on pnpm/yarn projects — runs npm install in project root

Summary

aiox update crashes on any project that uses pnpm (or yarn) as its package manager. The updater unconditionally shells out to npm install <pkg> --save-exact in the project root (process.cwd()). When the project's node_modules was created by pnpm, npm's @npmcli/arborist cannot read the dependency tree and throws, so the whole update aborts (then rolls back).

The update never gets to the part that actually matters — copying the framework files — because it dies on the package-install step.

Environment

aiox-core / @aiox-squads/core 5.2.9 (also reproduced on 5.0.7)
Node v24.13.0
npm v11.12.1
Project package manager [email protected] (declared via "packageManager" in package.json)
OS macOS (Darwin 25.5.0)

Steps to reproduce

  1. In any pnpm-managed project that has AIOX installed (.aiox-core/ present), where pnpm-lock.yaml and a pnpm-built node_modules/.pnpm/ exist.
  2. Run aiox update.

Expected

Framework files updated from the new @aiox-squads/core version; project config preserved.

Actual

🔄 AIOX Update

✗ Update failed

  Error: Command failed: npm install @aiox-squads/[email protected] --save-exact
  npm error Cannot read properties of null (reading 'matches')
  npm error A complete log of this run can be found in: ~/.npm/_logs/...-debug-0.log

  Rolled back to previous version.

npm debug log (key frames):

... silly placeDep ROOT @babel/[email protected] OK for: [email protected] want: ^7.12.1
... verbose stack TypeError: Cannot read properties of null (reading 'matches')
... verbose stack     at Link.matches (.../@npmcli/arborist/lib/node.js:1183:41)
... verbose stack     at Link.canDedupe (.../@npmcli/arborist/lib/node.js:1127:15)
... verbose stack     at PlaceDep.pruneDedupable (.../@npmcli/arborist/lib/place-dep.js:426:14)
...
... silly unfinished npm timer idealTree:node_modules/.pnpm/[email protected]/node_modules/cac
... verbose cwd /path/to/project

The .pnpm/... path and the unrelated project devDeps (@babel/*, jest, ts-node, typedoc) in the resolution confirm npm is trying to reconcile a pnpm-built tree in the project root and failing.

Root cause

packages/installer/src/updater/index.js, AIOXUpdater.applyUpdate():

const npmOptions = {
  cwd: this.projectRoot,          // <-- project root, may be pnpm/yarn-managed
  stdio: this.options.verbose ? 'inherit' : 'pipe',
  timeout: 120000,
};

if (previousCorePackage && previousCorePackage.packageName !== CORE_PACKAGE_NAME) {
  execFileSync('npm', ['uninstall', previousCorePackage.packageName], npmOptions);
}

const packageSpecifier = `${CORE_PACKAGE_NAME}@${targetVersion}`;
execFileSync('npm', ['install', packageSpecifier, '--save-exact'], npmOptions);   // <-- crashes on pnpm trees

The install is hardcoded to npm, in the project root, regardless of the project's package manager. Everything after this line (getPackageRootgenerateUpgradeReportapplyUpgrade file copy) is correct and package-manager-agnostic — it's only the install step that breaks.

Suggested fixes (any one resolves it)

  1. Detect the package manager (from packageManager field, or presence of pnpm-lock.yaml / yarn.lock) and dispatch the equivalent command (pnpm add -E <pkg>, yarn add -E <pkg>).

  2. Don't install into the project root at all. The updater only needs the package contents to copy .aiox-core/ from. Resolve/download the tarball to a temp dir (or reuse a globally installed @aiox-squads/core) and point getPackageRoot / applyUpgrade at that staged source. This avoids touching the project's node_modules and lockfile entirely — which is cleaner anyway, since most projects don't want @aiox-squads/core as a project dependency.

  3. At minimum: add --no-package-lock --no-save style isolation and fall back gracefully (or emit a clear error telling the user to update manually) when the install step fails, instead of only saying "Cannot read properties of null".

Workaround (for affected users)

Update the global CLI, then overlay the package .aiox-core/ manually, preserving project config:

# 1. Update global CLI (run from $HOME, not the project)
npm install -g aiox-core@latest

# 2. Stage the new package source
mkdir -p ~/.aiox-staging && cd ~/.aiox-staging
npm pack @aiox-squads/core@<version> && tar xzf aiox-squads-core-*.tgz

# 3. Back up, then overlay package files over .aiox-core/, then restore your config
cp -a /path/to/project/.aiox-core ~/.aiox-core.bak
cp -a package/.aiox-core/. /path/to/project/.aiox-core/
# restore core-config.yaml / pro-config.yaml / feature-registry.yaml / version.json from the backup

# 4. Install the .aiox-core isolated deps with npm (its own node_modules + package-lock.json — safe)
cd /path/to/project/.aiox-core && npm install --omit=dev

# 5. Verify
cd /path/to/project && aiox validate && aiox doctor

Note: .aiox-core/ has its own package-lock.json and isolated node_modules, so npm install inside .aiox-core is safe — it's only npm install in the project root (pnpm-managed) that breaks.

Impact

Blocks aiox update for every pnpm/yarn user. pnpm is common in monorepos, which are exactly the kind of projects likely to adopt AIOX.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions