Current Behavior
nx migrate latest (and nx migrate <version>) ignores the registries.default setting from pnpm-workspace.yaml and falls back to https://registry.npmjs.org. This affects two phases of the migration:
-
Temp directory install phase — nxCliPath() calls copyPackageManagerConfigurationFiles() which copies .npmrc but not pnpm-workspace.yaml to the temp directory. Since pnpm v11, registry config belongs in pnpm-workspace.yaml via registries.default. Without that file in the temp dir, pnpm falls back to npmjs.org.
-
Version resolution phase — packageRegistryView() runs pnpm view nx@latest version without passing cwd: workspaceRoot to execAsync. While the child process typically inherits the parent's cwd, this is fragile and doesn't guarantee pnpm-workspace.yaml is discoverable.
$ NX_SKIP_PROVENANCE_CHECK=true pnpm nx migrate 22.7.5 --verbose
[WARN] GET https://registry.npmjs.org/nx error (UNABLE_TO_GET_ISSUER_CERT_LOCALLY). Will retry in 10 seconds. 2 retries left.
Even when registry= is added to .npmrc as a workaround (which fixes phase 1), phase 2 still fails:
NX Unable to resolve version nx@latest.
Error: Unable to resolve version nx@latest.
at resolvePackageVersionUsingRegistry (/.../nx/dist/src/utils/package-manager.js:428:15)
at async parseTargetPackageAndVersion (/.../nx/dist/src/command-line/migrate/migrate.js:539:35)
Expected Behavior
nx migrate should respect registries.default from pnpm-workspace.yaml — the canonical location for registry configuration in pnpm v11+. Users who configure their registry exclusively via pnpm-workspace.yaml (without duplicating in .npmrc) should be able to run migrations successfully.
GitHub Repo
Any pnpm v11 workspace with registries.default in pnpm-workspace.yaml and no registry= in .npmrc.
Steps to Reproduce
-
Create a workspace with pnpm v11 and pnpm-workspace.yaml:
registries:
default: https://your-private-registry.example.com/npm/
-
Ensure there is no registry= line in .npmrc (or no .npmrc at all).
-
Run:
-
Observe: pnpm attempts to fetch from https://registry.npmjs.org instead of the configured private registry.
Nx Report
Nx: 22.7.5
pnpm: 11.5.0
OS: macOS (arm64)
Failure Logs
$ NX_SKIP_PROVENANCE_CHECK=true pnpm nx migrate latest --verbose
[WARN] GET https://registry.npmjs.org/nx error (UNABLE_TO_GET_ISSUER_CERT_LOCALLY). Will retry in 10 seconds. 2 retries left.
[WARN] GET https://registry.npmjs.org/nx error (UNABLE_TO_GET_ISSUER_CERT_LOCALLY). Will retry in 1 minute. 1 retries left.
# After adding registry= to .npmrc as workaround:
$ NX_SKIP_PROVENANCE_CHECK=true pnpm nx migrate latest --verbose
Packages: +112
Done in 5s using pnpm v11.5.0
NX Unable to resolve version nx@latest.
Error: Unable to resolve version nx@latest.
at resolvePackageVersionUsingRegistry (/private/var/folders/.../T/tmp-29072-.../node_modules/.pnpm/nx@22.7.5/node_modules/nx/dist/src/utils/package-manager.js:428:15)
at async parseTargetPackageAndVersion (/private/var/folders/.../nx/dist/src/command-line/migrate/migrate.js:539:35)
Package Manager Version
pnpm 11.5.0
Operating System
Additional Information
The issue is in packages/nx/src/utils/package-manager.ts:
-
copyPackageManagerConfigurationFiles (line ~403) only copies .npmrc, .yarnrc, .yarnrc.yml, and bunfig.toml. It does not copy pnpm-workspace.yaml, which since pnpm v11 is the canonical source of registry configuration (registries.default).
-
packageRegistryView (line ~582) runs pnpm view without passing cwd:
const { stdout } = await execAsync(`${pm} view ${pkg}@${version} ${args}`, {
windowsHide: true,
});
It should pass { cwd: workspaceRoot, windowsHide: true } to ensure pnpm can discover the workspace config.
Suggested fix
One or more of:
- Pass
cwd: workspaceRoot to the execAsync call in packageRegistryView
- Read
registries.default from pnpm-workspace.yaml and pass it via npm_config_registry env variable to the spawned commands
- Copy a minimal
pnpm-workspace.yaml (containing only registries:) to the temp directory in copyPackageManagerConfigurationFiles
Current Behavior
nx migrate latest(andnx migrate <version>) ignores theregistries.defaultsetting frompnpm-workspace.yamland falls back tohttps://registry.npmjs.org. This affects two phases of the migration:Temp directory install phase —
nxCliPath()callscopyPackageManagerConfigurationFiles()which copies.npmrcbut notpnpm-workspace.yamlto the temp directory. Since pnpm v11, registry config belongs inpnpm-workspace.yamlviaregistries.default. Without that file in the temp dir, pnpm falls back to npmjs.org.Version resolution phase —
packageRegistryView()runspnpm view nx@latest versionwithout passingcwd: workspaceRoottoexecAsync. While the child process typically inherits the parent's cwd, this is fragile and doesn't guaranteepnpm-workspace.yamlis discoverable.Even when
registry=is added to.npmrcas a workaround (which fixes phase 1), phase 2 still fails:Expected Behavior
nx migrateshould respectregistries.defaultfrompnpm-workspace.yaml— the canonical location for registry configuration in pnpm v11+. Users who configure their registry exclusively viapnpm-workspace.yaml(without duplicating in.npmrc) should be able to run migrations successfully.GitHub Repo
Any pnpm v11 workspace with
registries.defaultinpnpm-workspace.yamland noregistry=in.npmrc.Steps to Reproduce
Create a workspace with pnpm v11 and
pnpm-workspace.yaml:Ensure there is no
registry=line in.npmrc(or no.npmrcat all).Run:
Observe: pnpm attempts to fetch from
https://registry.npmjs.orginstead of the configured private registry.Nx Report
Failure Logs
Package Manager Version
pnpm 11.5.0
Operating System
Additional Information
The issue is in
packages/nx/src/utils/package-manager.ts:copyPackageManagerConfigurationFiles(line ~403) only copies.npmrc,.yarnrc,.yarnrc.yml, andbunfig.toml. It does not copypnpm-workspace.yaml, which since pnpm v11 is the canonical source of registry configuration (registries.default).packageRegistryView(line ~582) runspnpm viewwithout passingcwd:It should pass
{ cwd: workspaceRoot, windowsHide: true }to ensure pnpm can discover the workspace config.Suggested fix
One or more of:
cwd: workspaceRootto theexecAsynccall inpackageRegistryViewregistries.defaultfrompnpm-workspace.yamland pass it vianpm_config_registryenv variable to the spawned commandspnpm-workspace.yaml(containing onlyregistries:) to the temp directory incopyPackageManagerConfigurationFiles