Skip to content

Fix: npm ERR! code E404 – Not Found (package not found in registry)

FixDevs · (Updated: )

Part of:  JavaScript & TypeScript Errors

Quick Answer

How to fix npm ERR code E404 not found error caused by typos, private registries, scoped packages, deleted packages, and authentication issues.

The Error

You run npm install and get:

npm ERR! code E404
npm ERR! 404 Not Found - GET https://registry.npmjs.org/my-package - Not found
npm ERR! 404
npm ERR! 404  'my-package@latest' is not in this registry.

Or variations:

npm ERR! code E404
npm ERR! 404 Not Found - GET https://registry.npmjs.org/@myorg%2fmy-package - Not found
npm ERR! code E404
npm ERR! 404 Not Found - PUT https://registry.npmjs.org/my-package - Not found
npm ERR! code E404
npm ERR! 404 '@myorg/my-package@^2.0.0' is not in the npm registry.

npm cannot find the package in the registry. The package name does not exist, is misspelled, has been unpublished, or the registry configuration is wrong.

Why This Happens

When you run npm install my-package, npm sends a request to the configured registry (default: https://registry.npmjs.org/) to fetch the package metadata. A 404 response means the registry does not have a package with that name.

There are two things to keep separate. First, the package name might be unknown to the registry — it never existed, it was unpublished, or it lives in a different registry than the one your .npmrc resolves to. Second, the exact version might be unknown even though the package name resolves — for example, the version in your package-lock.json was yanked by the maintainer and 404s on next install. Both produce the same code E404, but the diagnostic path is different: name 404s usually point to a typo or registry misconfiguration, while version 404s point to a lockfile that captured an ephemeral state.

The registry itself is mutable. npm allows maintainers to unpublish packages under 72 hours old, and lets organizations remove or deprecate packages at any time. That means a build that worked yesterday can break today without any local change. This is the same class of incident that produced the original left-pad outage in 2016. The mitigation is to treat your lockfile as load-bearing and to mirror critical dependencies into a registry you control.

Common causes:

  • Package name typo. expres instead of express, loash instead of lodash.
  • Package was unpublished or removed. The author removed it from the registry.
  • Scoped package with wrong scope. @wrong-org/package instead of @correct-org/package.
  • Private package without authentication. You need to be logged in to access private packages.
  • Wrong registry configured. Your .npmrc points to a private registry that does not have the package.
  • Package renamed. The package was renamed and the old name is no longer available.
  • Publishing a scoped package without access. You try to publish @myorg/pkg but the organization does not exist or you do not have permission.

In Production: Incident Lens

The dangerous flavor of E404 in production is not the typo — it is “a transitive dependency we never named was unpublished or yanked, and now every deploy fails.” The build worked last night, the application code did not change, and yet npm ci exits non-zero in CI because package-lock.json pins a version of a 3-level-deep dependency that is no longer in the registry. The blast radius is “all new deploys fail until the lockfile is patched.” Hotfixes, rollbacks of older code, and new feature merges all get stuck behind the same broken npm ci step. If your incident response runbook assumes you can deploy a fix in five minutes, this incident makes that assumption invalid.

The monitoring signal lives in your CI/CD platform, not your application telemetry. Set up an alert on deploy-job failure rate per repo — a sudden jump in code E404 across multiple branches that share a common dependency is the fingerprint of an upstream yank. Pair that with an out-of-band synthetic build (a nightly npm ci against the latest main from a clean cache) that emails on failure. By the time a developer rage-pushes a fix at 3am, you want to already know which package disappeared.

The recovery sequence has three steps. First, identify the dropped package: npm ci prints the failing URL; npm view <pkg>@<version> confirms whether the version is gone. Second, pick a substitution path: pin to the nearest available version, or use overrides in package.json to redirect every consumer of the missing version to a safe one. Third, regenerate the lockfile and ship. The postmortem preventive is to stand up a private mirror (Verdaccio, JFrog Artifactory, GitHub Packages, or AWS CodeArtifact) configured as an upstream proxy to npmjs.org. The mirror caches every tarball you ever installed, so even if npmjs.org loses the file, your builds continue. Document the overrides and substitution recipes in the runbook so the on-call engineer does not have to invent them at 3am.

Fix 1: Check the Package Name for Typos

The most common cause. Verify the exact package name:

# Search for the package on npm
npm search my-package

# Or check directly on the npm website
# https://www.npmjs.com/package/my-package

Common typos:

WrongCorrect
expresexpress
loashlodash
axoisaxios
react-router-v6react-router
@types/reac@types/react
babel-core@babel/core (scoped since v7)
eslint-plugin-react-hookeslint-plugin-react-hooks

Check your package.json:

# Look for the problematic package
cat package.json | grep -i "the-package-name"

Fix the typo and run npm install again.

Pro Tip: If you are not sure about a package name, search for it with npm search <keyword> or browse npmjs.com. Partial name searches work: npm search react router finds react-router and related packages.

Fix 2: Check if the Package Was Renamed or Moved

Many popular packages have been renamed or moved to scoped packages:

# Old name → New name (scoped)
babel-core @babel/core
babel-preset-env @babel/preset-env
eslint-plugin-node eslint-plugin-n
webpack-cli @webpack-cli/serve (partially)

# Check if the old name has a deprecation message
npm view babel-core

npm will often show a deprecation notice:

npm WARN deprecated [email protected]: babel-core has been renamed to @babel/core

Fix: Update to the new package name:

npm uninstall babel-core
npm install @babel/core

Fix 3: Fix Registry Configuration

Your npm might be configured to use a private registry that does not have the package:

Check your current registry:

npm config get registry

The default is https://registry.npmjs.org/.

Check for .npmrc files:

# Project-level .npmrc
cat .npmrc

# User-level .npmrc
cat ~/.npmrc

# Global .npmrc
npm config list -l | grep registry

If a private registry is configured:

# .npmrc pointing to a private registry
registry=https://npm.mycompany.com/

# This means ALL packages are fetched from the private registry
# If your private registry doesn't proxy npmjs.org, public packages return 404

Fix: Use scoped registries instead of overriding the default:

# .npmrc — only route @myorg packages to the private registry
@myorg:registry=https://npm.mycompany.com/
# All other packages use the default npmjs.org registry

Fix: Reset to the default registry:

npm config set registry https://registry.npmjs.org/

Fix 4: Fix Scoped Package Access

Scoped packages (@org/package) have special access rules:

Public scoped packages:

# These should work without authentication
npm install @angular/core
npm install @types/node

Private scoped packages:

# You must be logged in
npm login

# Or use an auth token in .npmrc
//registry.npmjs.org/:_authToken=YOUR_TOKEN

Organization packages:

# Check if you have access
npm access list packages @myorg

# If publishing a scoped package for the first time, it defaults to private
# Make it public:
npm publish --access public

Common issue — wrong scope:

# Wrong: The organization name is case-sensitive
npm install @MyOrg/package  # 404!

# Fixed: Use the correct case (usually lowercase)
npm install @myorg/package

Common Mistake: Confusing npm organization names with GitHub organization names. They can be different. Check the package’s npm page for the exact scope name.

Fix 5: Fix Authentication for Private Packages

Private packages require authentication:

Log in to npm:

npm login
# Enter username, password, and email

Use an authentication token:

# In .npmrc
//registry.npmjs.org/:_authToken=${NPM_TOKEN}

For GitHub Packages:

# .npmrc
@myorg:registry=https://npm.pkg.github.com/
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}

For CI/CD environments:

# GitHub Actions example
- name: Setup .npmrc
  run: |
    echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > .npmrc

- run: npm install

Check if your token is valid:

npm whoami
# Should print your username
# If it shows an error, your token is invalid or expired

Fix 6: Fix Version-Specific 404

The package exists but the specific version does not:

npm ERR! 404 '[email protected]' is not in this registry.

Check available versions:

npm view lodash versions --json

Fix: Use a valid version:

# Install the latest version
npm install lodash@latest

# Or a specific valid version
npm install [email protected]

Check for pre-release tags:

npm view lodash dist-tags
# Shows: latest, next, beta, etc.

npm install lodash@next  # Install the next version

Fix lock file version conflicts:

# The lock file might reference a version that no longer exists
# Delete lock file and node_modules, then reinstall
rm package-lock.json
rm -rf node_modules
npm install

Fix 7: Fix Publishing 404 Errors

If you get 404 when publishing:

npm ERR! code E404
npm ERR! 404 Not Found - PUT https://registry.npmjs.org/@myorg/my-package

Check if the organization exists:

# The organization must exist on npmjs.com
npm org ls myorg

For scoped packages, set the access level:

# Scoped packages default to restricted (private)
# If you don't have a paid account, publish as public
npm publish --access public

Check your package.json name field:

{
  "name": "@myorg/my-package",
  "version": "1.0.0"
}

The name must match an organization you have access to.

Check if the package name is taken:

npm view my-package
# If it shows package info, the name is taken

Fix 8: Handle Unpublished or Removed Packages

If a package was unpublished (rare but possible):

Check if the package ever existed:

npm view my-package

If it was recently unpublished:

npm has a 72-hour window where packages can be unpublished. After that, the name is blocked for 24 hours. The package might come back.

Find an alternative:

# Search for similar packages
npm search keywords:similar-functionality

# Check if a fork exists
npm search my-package-fork

Use a specific tarball or git URL as a workaround:

{
  "dependencies": {
    "my-package": "github:user/my-package#v1.0.0",
    "other-package": "https://example.com/package-1.0.0.tgz"
  }
}

Still Not Working?

Check npm status. The registry might be experiencing issues:

# Check npm operational status
curl -I https://registry.npmjs.org/express

Clear the npm cache:

npm cache clean --force

Try with verbose logging:

npm install my-package --verbose

This shows the exact URL npm is requesting, which helps identify registry or proxy issues.

Check for proxy/VPN interference. Corporate proxies might block or redirect npm registry requests:

npm config set proxy http://proxy.mycompany.com:8080
npm config set https-proxy http://proxy.mycompany.com:8080

Check for DNS issues:

nslookup registry.npmjs.org

Use overrides to substitute a missing transitive dependency. If a deep dependency was yanked, you do not need to wait for every intermediate package to release a patch. Pin the substitute in your top-level package.json:

{
  "overrides": {
    "broken-pkg": "1.2.3"
  }
}

Yarn uses resolutions for the same purpose. After editing, delete the lockfile and run npm install to regenerate it. Re-run npm ci to confirm the build is green again.

Pin to commit SHA, not branch. If you depend on a git URL, github:user/repo#main resolves to whatever HEAD is today and 404s if the branch is renamed. Pin to a tag or commit SHA: github:user/repo#v1.2.3 or github:user/repo#a1b2c3d. This same lesson applies to private mirrors — pin the mirror’s upstream by digest, not by tag.

Audit your registry resolution at install time. Run npm config list and confirm the exact registry being used for the failing package. A .npmrc checked into the repo by another developer can override the user-level config without your knowledge. CI runners that inherit environment variables may end up with a stale NPM_CONFIG_REGISTRY pointing at a registry the package was never published to.

For other npm errors, see Fix: npm ERR! ERESOLVE unable to resolve dependency tree. For permission errors when installing packages globally, see Fix: npm EACCES permission denied global install. For npm lifecycle errors, see Fix: npm ERR! code ELIFECYCLE. For yarn integrity mismatches that surface after registry changes, see Fix: yarn integrity check failed.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles