npm Optional Dependency Skipped for Platform, Then Required at Runtime
npm marks platform-specific packages as optionalDependencies, then silently skips the ones whose os/cpu does not match the runner. The install passes, but a later step that genuinely needs that native binary fails because it was never put on disk.
What this error means
npm install prints "skipping optional dependency" for a platform-tagged package, the install exits 0, then a build or test step crashes with a missing-module or missing-binary error for that exact package.
npm warn optional SKIPPING OPTIONAL DEPENDENCY: @rollup/rollup-linux-x64-gnu@4.21.0
Error: Cannot find module @rollup/rollup-linux-x64-gnu.
npm has a bug related to optional dependencies (https://github.com/npm/cli/issues/4828).Common causes
A stale lockfile pins the wrong-platform optional binary
package-lock.json was generated on macOS or Windows, so it records the darwin/win32 optional binary. On the Linux runner npm skips it as not-applicable and the matching linux binary is never resolved.
npm optional-dependency resolution bug after a partial install
A previously cached node_modules or a half-deleted lockfile entry leaves npm unable to re-resolve the correct os/cpu variant, so it skips the optional dep that the platform actually requires.
os/cpu fields exclude the runner
The package declares os or cpu constraints (musl vs glibc, arm64 vs x64) that the runner does not satisfy, so no compatible optional binary exists to install.
How to fix it
Regenerate the lockfile so it carries every platform variant
- Delete node_modules and package-lock.json locally.
- Run npm install to rebuild a lockfile that records all optional os/cpu variants.
- Commit the regenerated lockfile so the Linux variant is present for CI.
rm -rf node_modules package-lock.json
npm install
git add package-lock.jsonClean-install on a cache miss instead of reusing a foreign node_modules
Make sure CI does a clean npm ci against a lockfile generated for (or inclusive of) the runner platform, and does not restore a node_modules cached from a different OS.
- run: npm ci
# key the node_modules cache on os + lockfile so a darwin tree is never reused on linux
- uses: actions/cache@v4
with:
path: ~/.npm
key: npm-${{ runner.os }}-${{ hashFiles('package-lock.json') }}Match the runner libc and arch to the available binaries
- Confirm whether the package ships a musl (Alpine) or glibc build for your arch.
- On Alpine, switch to a glibc base image or install the musl variant the package provides.
- Pin the runner architecture so the optional binary that matches is the one npm resolves.
How to prevent it
- Commit a lockfile generated with a recent npm that records all optional os/cpu variants, run npm ci on a clean tree in CI, and never restore a node_modules cache across operating systems.