Troubleshooting
Try disabling the global virtual store first
If an install or build is behaving oddly, turn the global virtual store off for the project before digging further:
scpm config set enableGlobalVirtualStore false --location project
Symptoms that usually point here:
Symlink ... is invalid, it points out of the filesystem rootENOENT: no such file or directoryfor a module that clearly exists undernode_modules/.scpm/Cannot find module '<pkg>'from Next.js / Turbopack, Vite, VitePress, Nuxt, or Parcel during dev or build- Plugin config discovery (PostCSS, Tailwind, Vite) silently misses a config file that lives at the project root
ERR_INVALID_PACKAGE_TARGETor exports-resolution failures for a package that resolves fine under pnpm/npm
See Global virtual store for what this changes.
A package is missing from node_modules
Run:
scpm install
scpm list --depth Infinity <package>
In the isolated linker, only direct dependencies are symlinked at the top level.
Transitive dependencies live under the packages that declared them. If your app
imports a package directly, add it to your own package.json.
A package config file cannot resolve its own dependency
Some tools discover dependency-owned config files by walking
node_modules/<pkg>/... and then evaluating the config file at that symlink
path. In an isolated linker, the package's own dependencies are linked next to
the package's real virtual-store path under node_modules/.scpm/, not next to
the top-level symlink.
That means a package config file can fail to require() a dependency that the
same package declares, even though the package was installed correctly. Some
native or framework toolchains swallow the config-load error and fail later
with generated code or build output that looks unrelated.
This usually means the loader is not symlink-aware; it does not mean scpm linked the package incorrectly.
If you maintain the loader, resolve the config file's real path before evaluating it:
const configPath = fs.realpathSync(discoveredConfigPath);
When reporting this upstream, ask the tool maintainer to load
dependency-owned config files from their real path, or to create the require
function from the real config path, before evaluating the file. Include a small
repro that shows the config succeeds after fs.realpathSync(...).
If the toolchain cannot be changed, use hoisted mode for that project:
scpm install --node-linker=hoisted
Hoisted mode gives legacy tooling an npm-style tree where more packages are
visible from top-level node_modules paths. Treat it as a compatibility
workaround for loaders that are not symlink-aware.
A dependency build script did not run
Dependency lifecycle scripts follow the pnpm v11 build approval model. Inspect the pending list:
scpm ignored-builds
scpm approve-builds
scpm rebuild
Use --dangerously-allow-all-builds only for a local diagnostic run. Do not use
it as a permanent CI default.
A jailed dependency build needs more access
If jailBuilds is enabled and an approved dependency build fails only inside
the jail, keep the jail on and grant the narrow permission the package needs:
jailBuildPermissions:
"@vendor/*":
env:
- SOME_BUILD_FLAG
write:
- ~/.cache/vendor
If the package cannot run in the jail yet, disable the jail for that package glob without bypassing build approval:
jailBuildExclusions:
- "@legacy-native/*"
See Jailed builds for the default profile and supported permission keys.
A lockfile format is unsupported
scpm reads and writes the current supported lockfile formats listed on the
lockfiles page. Older pnpm v5/v6 lockfiles
should be upgraded with pnpm first. Yarn PnP projects need to switch to
nodeLinker: node-modules before using scpm — scpm writes a regular
node_modules tree, not .pnp.cjs.
If a project has multiple lockfiles, keep one canonical lockfile before rolling scpm into CI.
The lockfile is out of sync
For a strict CI install:
scpm ci
For a local repair:
scpm install --fix-lockfile
For a full re-resolve:
scpm install --no-frozen-lockfile
scpm run installed unexpectedly
Script commands check install freshness first. If package.json or the
lockfile changed, scpm installs before running the script:
scpm run build
scpm test
If you want to skip that check for one command:
scpm run --no-install build
scpm test --no-install
The registry is unavailable
If the cache already contains the required metadata and tarballs:
scpm install --offline
If you want to prefer cached metadata but allow network misses:
scpm install --prefer-offline
A private registry or scope is not authenticated
scpm reads npm-compatible registry configuration from .npmrc and environment
variables.
@myorg:registry=https://registry.example.test
//registry.example.test/:_authToken=${NPM_TOKEN}
Check what scpm sees:
scpm config get registry
scpm config list --json
For publishing and login flows, see Registry and auth.
A workspace filter matches nothing
Check the workspace package names and paths:
scpm list -r --depth 0
Then retry with an exact name, glob, path selector, or graph selector:
scpm -F '@scope/*' run build
scpm -F './packages/api' test
scpm -F 'api...' run build
You need to fall back temporarily
Keep the existing lockfile while evaluating scpm. Since scpm writes supported lockfiles in place, the original package manager can keep using the same file during rollout. If scpm hits a bug in a project, fall back for that job, keep the failing command and lockfile handy, and open a thread in GitHub Discussions with the exact command and package manager versions.
