Fix Firebase Functions Deploy: Missing: X from lock file
The build server runs npm ci with a stricter config than your laptop. Here is why that produces dozens of "Missing" lines on a lockfile that installs fine locally, reproduced end to end.
firebase deploy --only functions fails during the Cloud Build step with a wall of lines like
this, even though the same project installs cleanly on your machine:
Build failed: ...
npm ERR! Missing: regex-range@5.0.1 from lock file
npm ERR! Missing: brace-expansion@1.1.11 from lock file
npm ERR! Missing: balanced-match@1.0.2 from lock file
npm ERR! Missing: p-limit@2.3.0 from lock file
npm ERR! Missing: react-is@18.2.0 from lock file
npm ERR! Missing: kleur@3.0.3 from lock file
npm ERR!
npm ERR! Clean install a project
npm ERR!
npm ERR! Usage:
npm ERR! npm ciTL;DR#
The build server runs npm ci, and npm ci refuses to proceed unless package-lock.json
matches package.json exactly under the npm config it runs with. If your lock file was resolved
with legacy-peer-deps enabled — set globally, or left behind by a one-off
npm install --legacy-peer-deps you ran earlier to clear an ERESOLVE error — it never recorded
some peer dependencies (commonly jest, pulled in by firebase-functions-test). The build
server's npm ci runs with npm's default, legacy-peer-deps=false, expects those packages, and
fails. Fix it by either shipping a functions/.npmrc with legacy-peer-deps=true so the build
resolves the lock file the same way you did, or by unsetting the flag and regenerating the lock
file with a plain npm install.
Why it happens#
This is a real Stack Overflow question with an accepted answer
(75969901,
8 votes, over 4,000 views) and the linked GitHub issue,
firebase-tools#5673, has collected
reports for more than three years and is still open as of this writing. Most of the thread's
early answers (downgrade firebase-tools, pin Node 16) treat it as a Firebase CLI bug. A comment
posted on the issue on 11 August narrows it to one well-defined, reproducible cause that
explains most of the reports, and the same author opened
firebase-tools PR #10918 (still open,
draft, as of this writing) to have the CLI warn about it before upload instead of letting a
multi-minute Cloud Build fail first.
npm ci is stricter than npm install: it reads package-lock.json as the complete, final
dependency tree and errors out the moment an entry it expects is not there — it does not try to
resolve anything itself. What it "expects" depends on the npm config in effect when npm ci
runs, and legacy-peer-deps changes that.
- With
legacy-peer-deps=false(npm's default since npm 7), npm installs a package's non-optional peer dependencies automatically, and records them in the lock file. - With
legacy-peer-deps=true, npm skips peer-dependency resolution entirely — the old npm 6 behaviour — and the lock file is written without those entries.
A stock firebase init functions project is enough to hit this: firebase-functions-test, which
the CLI adds as a dev dependency by default, declares jest as a non-optional peer dependency.
If the lock file was produced under legacy-peer-deps=true, it has no resolved entry for jest
(or its own dependency tree — strip-ansi, wrap-ansi, lru-cache and the rest of the lines in
the error above). Locally that lock file installs without complaint, because your local config
still has legacy-peer-deps=true and npm ci/npm install never look for the peer. The Cloud
Build server runs npm ci with the default, legacy-peer-deps=false, which does look for it —
and the lock file does not have it.
The flag does not have to be set on purpose. It is a common one-off fix for an ERESOLVE
conflict (npm install --legacy-peer-deps), and that single install call leaves the lock file in
exactly the same state as a permanent config change, without saving anything to .npmrc or to
your npm config — which is why checking npm config get legacy-peer-deps can still come back
false for someone who hit this.
Reproducing it#
Reproduced on Node 20.20.2 / npm 10.8.2, in a disposable node:20 Docker container, with a
functions/package.json scaffolded the way firebase init functions leaves it: firebase-admin
and firebase-functions as dependencies, firebase-functions-test as the only dev dependency
(no explicit jest entry — it only exists as firebase-functions-test's peer dependency).
1. Resolve the lock file with legacy-peer-deps enabled — the one-off workaround, or a
pre-existing config:
npm install --legacy-peer-depsChecking the resulting package-lock.json confirms jest is referenced only as a requirement,
with no resolved package for it:
jest resolved in lockfile: false
firebase-functions-test peerDependencies: {"firebase-admin":"^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0","firebase-functions":">=4.9.0","jest":">=28.0.0"}2. Simulate the build server: delete node_modules, confirm the default config, and run
npm ci on that same lock file:
npm config get legacy-peer-deps -> false
npm error Missing: strip-ansi@7.2.0 from lock file
npm error Missing: strip-ansi-cjs@6.0.1 from lock file
npm error Missing: wrap-ansi@8.1.0 from lock file
npm error Missing: wrap-ansi-cjs@7.0.0 from lock file
npm error Missing: eastasianwidth@0.2.0 from lock file
npm error Missing: emoji-regex@9.2.2 from lock file
npm error Missing: ansi-regex@6.4.0 from lock file
npm error Missing: ansi-styles@6.2.3 from lock file
npm error Missing: lru-cache@10.4.3 from lock file
npm error Missing: brace-expansion@2.1.7 from lock file
npm error Missing: balanced-match@1.0.2 from lock file
npm error
npm error Clean install a projectThat is the exact family of errors from the Stack Overflow question and the GitHub issue, on a project with nothing wrong in its source code — reproduced by changing one thing, which npm config resolved the lock file.
3. Fix A — ship a functions/.npmrc with the same setting your local install used, so the
build server resolves the lock file the same way:
legacy-peer-deps=truenpm ci on the exact same lock file, with only that file added, now succeeds:
added 243 packages, and audited 244 packages in 13s4. Fix B (negative control) — the alternative: drop legacy-peer-deps entirely and
regenerate the lock file with a plain npm install. npm ci on the new lock file installs
jest and its whole dependency tree, which were absent before, and succeeds cleanly with the
default config — no .npmrc needed:
npm config get legacy-peer-deps -> false
added 506 packages, and audited 507 packages in 30sBoth fixes change exactly one thing and both produce a passing npm ci on the condition the
error happens under: the default, non-legacy peer-dependency resolution the build server uses.
Fix#
Pick one, depending on whether you rely on legacy-peer-deps for a real reason (an unresolved
peer conflict elsewhere in the tree) or only hit it by accident.
Option A — keep legacy-peer-deps, make the build match it. Check first:
npm config get legacy-peer-depsIf that prints true, create functions/.npmrc (it gets uploaded with your function source, so
Cloud Build reads it):
legacy-peer-deps=trueIf it prints false, the flag may still have been used once, for an install that is now baked
into your lock file. Re-run npm install --legacy-peer-deps in functions/ to put the lock file
back in a consistent state before adding the same .npmrc.
Option B — drop the flag, regenerate the lock file:
cd functions
npm config delete legacy-peer-deps # only if it was set globally
rm package-lock.json
npm installCommit the new package-lock.json and deploy again. This is the fix to prefer when nothing in
your dependency tree actually needs legacy-peer-deps — it keeps the lock file aligned with the
config every environment uses by default, instead of carrying a .npmrc to compensate.
Avoid deleting package-lock.json right before every deploy without regenerating it with the
matching config: it happens to work because Cloud Build falls back to npm install when no lock
file is present, but that re-resolves every version on every deploy and defeats the point of
locking dependencies in the first place. Several reporters on the GitHub issue settled on this as
a permanent habit; it is a symptom mask, not the fix.
Check your own npm output#
If what you are staring at is an ERESOLVE error rather than the Missing: … from lock file
error above — the one that usually leads someone to run npm install --legacy-peer-deps in the
first place — paste the conflicting-dependency output below to see which two packages disagree
and which version actually satisfies both, instead of reaching for the flag that causes the
deploy failure above. It runs in your browser; nothing is sent anywhere. To read that output by hand, the Conflicting peer dependency walkthrough for React 17 shows which line names the installed version and which package to update instead of bypassing peer checks.
Everything runs in your browser — this tool makes no network request with what you paste, and file paths from the log are never shown back. Registry tokens or passwords that slip into a paste are masked in the output.
Verify the fix#
After either fix, confirm locally that npm ci — not npm install — succeeds under the same
config Cloud Build uses: the default, legacy-peer-deps=false. If your local npm config still
has legacy-peer-deps=true (Option A's reason for existing), a plain npm ci will pass on the
broken lock file too, because it inherits that same leniency — it is not testing what the build
server actually runs. Override it explicitly for this check:
cd functions
rm -rf node_modules
npm ci --legacy-peer-deps=falseA clean added N packages with no Missing: lines means the lock file now matches what
npm ci expects under the default config, which is what Cloud Build will run. Then redeploy:
firebase deploy --only functionsOne email a month — the latest explained errors
What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.
If the error is "Could not find functions.yaml. Must use http discovery"#
That line by itself is not the failure — it is a notice the CLI logs before it falls back to the
local HTTP discovery server. The accepted answer's own firebase-debug.log excerpt shows
discovery then succeeding on the same run: the CLI serves on port 8008, gets a response from
/__/functions.yaml, and packages the function for upload. The actual failure in that report is
the npm ci step that runs afterward during the Cloud Build upload, which is the
Missing: … from lock file error covered above. If you see this functions.yaml line in your own
firebase-debug.log, check whether discovery completed (a Serving at port 8008 /
Got response from /__/functions.yaml pair right after it) before assuming it is the cause —
fix the npm resolution first and re-check the log; most reports in the GitHub issue that mention
functions.yaml resolve once the Missing: … from lock file error is gone.
A different Firebase deploy failure that is also configuration-only, not a code bug, is covered in
Fix "Assertion failed: resolving hosting target" (Firebase).
If your functions are served behind a Next.js app on Firebase Hosting and the CI step itself
fails rather than the npm install, see
Firebase webframeworks Not Enabled in GitHub Actions: Fix.
A different, webpack-side "firebase breaks my build" failure — not an npm peer-dependency gap but
a package exports ordering bug in one Firebase release — is in
Fix "Default condition should be last one" (Firebase).
Once the deploy itself succeeds, a function that reads or writes Firestore can still fail at
request time with a security-rules rejection that looks unrelated to anything above — see
Firestore PERMISSION_DENIED: Every Real Cause and Fix.
Most reports of this error date from 2023, but the GitHub issue keeps collecting fresh comments
years later, which means the npm-level cause has outlived several major versions of both npm and
firebase-tools — it is a property of how npm ci validates a lock file against the active
config, not a bug tied to one release of either tool.
Frequently Asked Questions
Still stuck? Paste your error.
Paste the error message or stack trace — we check it against every fix and tool on the site.
Continue Reading
Fix npm EACCES: permission denied on package-lock.json
Separate a local lockfile permission failure from cache and global-install errors, then fix the owner without changing the dependency tree.
Fix: Local package.json exists, but node_modules missing
Why cloning a repository never brings its dependencies with it, and the one command that turns a missing-binary failure into a working start script.
Fix Docker npm EBADENGINE: Unsupported engine
Read the Required and Actual lines, distinguish npm self-upgrades from application installs, and stop a floating npm release breaking an unchanged Dockerfile.


