npm audit failure — do not delete package-lock.json to fix it
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.jsonand runningnpm install.
That makes the build pass, but it is the wrong fix and it is risky.
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: any7.xversion that is7.12.0or higher, but not8.0.0. - so
7.13.1,7.15.0, and7.16.0are all accepted.
- the
- 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 ourDockerfile) will not even run without it.
The rule that explains everything
When
package-lock.jsonexists,npm installandnpm ciinstall the version written in the lock file — not the newest version in the range — as long as the locked version is still allowed bypackage.json.
Read that again.
^7.12.0does 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.jsonsays^7.12.0.package-lock.jsonsays7.13.1.- Is
7.13.1still 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 fixdid nothing.npm audit fix --forcegave an error.
Why?
- The four
@react-router/*packages must all be the exact same version as each other. npm audit fixcannot 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.0forreact-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:
- It updates the range in
package.jsonto^7.16.0, so we never allow the old version again. - 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.jsonhanya untuk memperbaiki satu paket.
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.