npm ci Fails in Workers Builds but Works Locally: How to Match npm Versions
An npm version declared in engines gets an EBADENGINE warning from Workers Builds, which keeps using its own npm. Where does it actually read the version from?

- Workers Builds now supports selecting npm through packageManager in package.json.
- When npm ci succeeds locally but reports a lockfile mismatch remotely, compare npm versions.
- A version pin does not repair an inconsistent lockfile or mismatched install options.
engines.npm is not a version installer
Cloudflare’s October 23 change lets Workers Builds install the npm version specified in packageManager before dependency installation begins. Editing engines.npm does not select that version.
The build image documentation says an engines-only configuration leaves the default npm in place, with an EBADENGINE warning when the requirement does not match. Declaring a requirement and provisioning the required executable are different steps.
Use the exact npm version that handles your lockfile
Run npm -v in the project where installation works. Add that version to the top level of package.json. Cloudflare’s example looks like this:
{
"packageManager": "[email protected]"
} The number is an example, not a requirement to upgrade every project to 11.19.1. Use an exact version rather than a range such as npm@^11. Include the changed file in the commit being built, then inspect the build log for the selected tool and installation result.
This targets a specific failure described in Cloudflare’s troubleshooting guide: npm ci succeeds on your machine but Workers Builds reports that the package manifest and lockfile are out of sync. A newer local npm may have written a lockfile that the image’s default handles differently. Compare the tooling before treating that remote message as a reason to discard the lockfile.
A monorepo can expose two packageManager values
Workers Builds reads package.json from the configured project root. If that is below the repository root, it also reads the repository-level file. Keep the values identical when both declare packageManager.
The field selects a package-manager version; the repository’s lockfile still determines which manager installs dependencies. Changing the field alone is therefore not a complete migration from Yarn or pnpm to npm.
Node.js is configured separately through NODE_VERSION, .nvmrc or .node-version. Pinning npm should not be mistaken for pinning the Node version used during the build.
Check real dependency differences and install flags next
According to the npm ci reference, an actual mismatch between the dependency manifest and its lockfile is an error. The command does not rewrite either file to reconcile the difference.
Make the intended dependency changes locally, keep the manifest and lockfile together, and verify installation again. Choose the working directory deliberately: npm ci removes an existing node_modules before installing.
Options used to generate the dependency tree also matter. If the lockfile was created with --legacy-peer-deps or --install-links, npm says to provide the same flags to CI. Project settings can be committed in .npmrc. A reproducible installation needs agreement across the tool version, dependency files and relevant options.