Skip to main content

npm audit failure — do not delete package-lock.json to fix it

· 4 min read

Today ci:security failed (the step that runs npm audit).

  • The real fix is one small change.
  • But I saw a branch that "fixed" it by deleting package-lock.json and running npm install.

That makes the build pass, but it is the wrong fix and it is risky.

warning

I want everyone to understand why, because this is about how npm chooses versions.

If you do not understand this, you will break things later.

Two files, two different jobs​

package.json is the range we accept.

  • When we write "react-router": "^7.12.0":
    • the ^ means: any 7.x version that is 7.12.0 or higher, but not 8.0.0.
    • so 7.13.1, 7.15.0, and 7.16.0 are all accepted.
  • It does not name one version. It names a set of allowed versions.

package-lock.json is the exact versions we actually installed.

  • Every package, direct and indirect, with its exact version number.
  • We commit this file so that everyone installs the exact same code:
    • your laptop
    • my laptop
    • CI
    • production Docker
  • npm ci (used in CI and in our Dockerfile) will not even run without it.

The rule that explains everything​

When package-lock.json exists, npm install and npm ci install the version written in the lock file — not the newest version in the range — as long as the locked version is still allowed by package.json.

Read that again.

  • ^7.12.0 does not mean "always give me the newest 7.x".
  • It means "the newest 7.x is allowed".
  • Allowed is not the same as chosen.
  • Once the lock file exists, the lock file chooses.

So why were we stuck on the old version?​

  • package.json says ^7.12.0.
  • package-lock.json says 7.13.1.
  • Is 7.13.1 still allowed by ^7.12.0? Yes.
  • So npm keeps 7.13.1. Every install. Every deploy.

The fixed version 7.16.0 was already available the whole time, and it is also allowed by ^7.12.0.

  • But npm never picks it, because a locked version is never upgraded on its own.
  • That is the whole reason the build kept failing while the fix was already allowed.

Why npm audit fix did not help​

  • npm audit fix did nothing.
  • npm audit fix --force gave an error.

Why?

  • The four @react-router/* packages must all be the exact same version as each other.
  • npm audit fix cannot upgrade them together safely.
  • That is why it felt like deleting the lock file was the only option. It was not.

What deleting package-lock.json actually did​

When you delete the lock file, npm has no recorded versions to follow.

  • So npm chooses every version again, from the ranges.
  • "Newest allowed" becomes 7.16.0 for react-router. Good — that part is fixed.

But you did not only upgrade react-router:

  • npm chose a new version for every package in the project — hundreds of them.
  • Each one jumped to the newest version its own range allows.
  • You wanted to change one thing, and you changed everything.

The result:

  • The diff is huge.
  • The one change you wanted is hidden inside hundreds you did not ask for.
  • Any one of those changes you did not notice can break production.
  • You do not know what you installed.

The correct fix — change only the packages you mean to change​

npm install \
react-router@^7.16.0 \
@react-router/node@^7.16.0 \
@react-router/serve@^7.16.0 \
@react-router/dev@^7.16.0

This does two things, and nothing else:

  1. It updates the range in package.json to ^7.16.0, so we never allow the old version again.
  2. It updates only those four packages in the lock file. Everything else stays exactly where it was.

Now:

  • the diff is four lines
  • the build installs the same code everywhere
  • a reviewer can actually read it

The principle​

package-lock.json is the file that makes "it works" mean "it works the same everywhere".

  • It is not there to annoy you. It is doing an important job.
  • When one package is stuck, change that package on purpose.
  • Do not delete the record and reinstall everything from zero.

jangan hapus package-lock.json hanya untuk memperbaiki satu paket.

note

The EBADENGINE warning about Node v23.5.0 is not the cause of this failure.

  • Our production Docker uses Node 22.
  • Please use Node 22 (LTS) on your machine, so you stop seeing that warning.