Start with the request that returned 401
Errors such as npm error code E401, 401 Unauthorized, or authentication token not provided point to an authentication problem. A token can be valid but absent from the request because it belongs to a different hostname or was not available to the build process.
- Check the registry URL in the failed request, including its port and path.
- Check the token variable in the same shell or CI step, without printing its value.
- Use the authentication settings for your package manager; npm and pnpm handle project environment references differently.
- Check whether the command installs or publishes, then verify that credential's status and access.
The examples use @acme/sdk on registry.privatenpm.com. Substitute a package you can access and its actual registry. A key issued by npmjs.org or GitHub Packages will not authenticate to PrivateNPM.
1. Match the registry and credential host
Run these checks from the directory where the failing command runs:
npm config get @acme:registry
npm config get registry
npm config get userconfigFor this example, the scope route should show https://registry.privatenpm.com/. An undefined scope route falls back to the default registry. The userconfig result identifies the active user file, which CI may redirect to a temporary location.
For npm, this project .npmrc sends the scope and its credential to the same host:
@acme:registry=https://registry.privatenpm.com/
//registry.privatenpm.com/:_authToken=${ACME_NPM_TOKEN}A credential configured for registry.npmjs.org does not accompany a request to registry.privatenpm.com. With a custom domain, change both lines to that active hostname. Include an explicit port when the URL has one; for a registry served below a URL path, scope the credential to its documented registry path.
Check command flags and environment overrides as well as files. They can change the effective configuration; see npm's configuration precedence. If metadata succeeds but a tarball fails, inspect the failed tarball URL and any old registry URLs in the lockfile. A different download host needs the registry provider's documented setup.
2. Check for a missing environment token
A reference to ${ACME_NPM_TOKEN} does not create the variable. Supply a download key through your shell or CI secret store, then run this in the same process environment as the install:
node -e 'if (!process.env.ACME_NPM_TOKEN) { console.error("ACME_NPM_TOKEN is missing or empty"); process.exit(1); } console.log("ACME_NPM_TOKEN is set");'ACME_NPM_TOKEN is set confirms only that a value exists. It does not prove that the key is valid. If the check fails, fix the variable's name or availability before retrying. A value stored only in .env is not automatically part of npm's environment.
In CI, attach the secret to the step that installs dependencies. Check environment restrictions and whether that run is allowed to receive secrets; a local success does not prove a pull-request build has the same credentials. With actions/setup-node, use the expected NODE_AUTH_TOKEN variable as shown in the GitHub Actions setup.
Keep token values out of diagnostic output. Do not paste your complete configuration or environment into an issue. If the key is expired or revoked, issue a replacement with the required access and update the relevant secret.
3. Fix pnpm project .npmrc environment references
For pnpm 12, keep the scope route in the project's .npmrc, and remove a token line that relies on project-level environment expansion:
@acme:registry=https://registry.privatenpm.com/With ACME_NPM_TOKEN supplied by your secret store, use a host-scoped environment setting in a POSIX shell:
env "pnpm_config_//registry.privatenpm.com/:_authToken=$ACME_NPM_TOKEN" pnpm view @acme/sdk versionThe expected result is the package's published version. Since pnpm 11.5.3, environment references in project-level authentication settings are ignored. A user-level authentication file can still contain the reference; see the pnpm authentication documentation.
4. Separate install and publish credentials
If npm ci or an install fails, use a read-only download key with access to every private dependency involved. If npm publish fails, check publishConfig.registry in package.json as well as the scope route, then use a publisher key with permission to publish that package.
On PrivateNPM, download keys begin with pdk_ and publisher keys with prt_. A download key cannot publish. Open the registry's Manage keys page to check status and package or scope restrictions. A valid credential can still be refused for an operation it does not permit.
For CI releases, trusted publishing with OIDC authorizes publishing. It does not authenticate the earlier private dependency install. Supply its download key separately. For a rejected OIDC exchange, follow that guide's checks for repository, workflow, audience and CI identity.
Verify the fix before retrying the full build
For npm, after configuring the token, request metadata for a package you are allowed to read:
npm view @acme/sdk versionA version result confirms that this metadata request worked. It does not prove tarball access or publishing permission. Retry the original install from a clean checkout using its committed lockfile and the same credential. Keep your normal dependency release-age and lifecycle-script policies; an authentication fix does not require weakening them.
For pnpm, apply the credential to the install command too:
env "pnpm_config_//registry.privatenpm.com/:_authToken=$ACME_NPM_TOKEN" pnpm install --frozen-lockfile --ignore-scriptsIf you are testing a download-key change, use an empty temporary store so a cached copy cannot hide a denied download. To verify a publish, follow the publishing walkthrough with a new version of a package you control.
What if the error changes to 403 or 404?
- 401: the registry did not accept the credentials. On PrivateNPM this includes missing, invalid, expired and revoked keys.
- 403: inspect access rules and the response message. A PrivateNPM publish can also be blocked by a trial or plan requirement, or an already-published version.
- 404: check the registry, package name and version. Some registries conceal inaccessible packages with a 404, so verify access with the registry owner as well.
Seeing Unknown user config "always-auth" instead? Follow the always-auth warning fix. For the complete npm, pnpm, Yarn and CI configuration, return to the private registry .npmrc guide.