Supabase

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.

Mahdi Ben RhoumaMahdi Ben Rhouma
8 min read

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:

text
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 ci

TL;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:

bash
npm install --legacy-peer-deps

Checking the resulting package-lock.json confirms jest is referenced only as a requirement, with no resolved package for it:

text
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:

text
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 project

That 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:

text
legacy-peer-deps=true

npm ci on the exact same lock file, with only that file added, now succeeds:

text
added 243 packages, and audited 244 packages in 13s

4. 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:

text
npm config get legacy-peer-deps -> false
added 506 packages, and audited 507 packages in 30s

Both 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:

bash
npm config get legacy-peer-deps

If that prints true, create functions/.npmrc (it gets uploaded with your function source, so Cloud Build reads it):

text
legacy-peer-deps=true

If 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:

bash
cd functions
npm config delete legacy-peer-deps   # only if it was set globally
rm package-lock.json
npm install

Commit 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:

bash
cd functions
rm -rf node_modules
npm ci --legacy-peer-deps=false

A 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:

bash
firebase deploy --only functions

One 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

|

Have more questions? Contact us

Still stuck? Paste your error.

Paste the error message or stack trace — we check it against every fix and tool on the site.

Matching happens in your browser. When you search, a cleaned error signature is sent so we can spot errors we do not cover yet — never the raw text, your IP address or your e-mail. Do not paste passwords or keys. Details

Written by

Mahdi Ben Rhouma
Mahdi Ben Rhouma

Full-Stack Dev — Next.js & Supabase

Solo developer building SaaS products with Next.js and Supabase. Writing about production patterns the official docs skip.

Remote