Supabase

Firebase: Cannot read property 'getProvider' of undefined

Why passing firebaseConfig to getFirestore() breaks inside the Firebase SDK, reproduced on firebase 9.0.0 and 13.0.0, with the corrected Next.js module and the wrapper that only hid the bug.

Mahdi Ben RhoumaMahdi Ben Rhouma
7 min read

TL;DR#

getFirestore() takes a FirebaseApp, the object returned by initializeApp(). The code in the Stack Overflow question passes firebaseConfig, the plain options object, instead. The SDK treats that object as an app and immediately reads its internal container property, which a config object does not have, so JavaScript throws. Create the app once, keep the return value, and pass it: export const db = getFirestore(app). Nothing about Next.js or server-side rendering is involved; a plain Node.js script fails on the same line.

The module from the question#

The asker exports a Firestore instance from a shared module, so that pages can import db instead of setting Firebase up again in each file:

js
import { initializeApp, getApps, getApp } from "@firebase/firestore";
import { getFirestore } from "@firebase/firestore";
 
const firebaseConfig = {
  ...config
};
 
export default function firebaseApp() {
  if (!getApps().length) {
    firebaseApp = initializeApp(firebaseConfig);
  } else {
    firebaseApp = getApp(firebaseConfig);
  }
}
 
export const db = getFirestore(firebaseConfig)

Several things are off in this module, and only one of them produces the error in the title:

  1. export const db = getFirestore(firebaseConfig) passes the options object where the SDK expects an app. This is the line that throws, and it runs as soon as any file imports the module.
  2. initializeApp, getApps and getApp are imported from the Firestore package. They belong to firebase/app, as the Firebase app reference lists them.
  3. getApp() takes an app name, not the config; the reference describes it as returning the default app when called with no arguments.

The question's title carries the wording of older Node.js releases. The same two lines, initializeApp(firebaseConfig) followed by getFirestore(firebaseConfig), reproduce it word for word with firebase 9.0.0 on Node 14.21.3 (official node:14.21 Docker image, 10 October 2026), in a plain CommonJS script with no framework:

text
/w/node_modules/@firebase/app/dist/index.cjs.js:274
    return app.container.getProvider(name);
                         ^
 
TypeError: Cannot read property 'getProvider' of undefined
    at Object._getProvider (/w/node_modules/@firebase/app/dist/index.cjs.js:274:26)
    at getFirestore (/w/node_modules/@firebase/firestore/dist/index.node.cjs.js:22389:16)

On a current Node.js the message is worded differently, but it is the same failure. Reproduced with firebase 13.0.0 on Node 22.23.3 (official node:22-bookworm-slim image, same day), in a plain ES module:

text
file:///w/node_modules/@firebase/app/dist/esm/index.esm.js:272
        .getProvider('heartbeat')
         ^
 
TypeError: Cannot read properties of undefined (reading 'getProvider')
    at _getProvider (file:///w/node_modules/@firebase/app/dist/esm/index.esm.js:272:10)
    at getFirestore (file:///w/node_modules/@firebase/firestore/dist/common-C1sORuFu.node.mjs:42237:16)
    at file:///w/broken.mjs:12:19

Why the SDK reads a property your config does not have#

The reference for getFirestore(app) declares a single parameter of type FirebaseApp. A FirebaseApp is more than its options: the SDK attaches an internal component container to every app it creates, and each service (Firestore, Auth, Storage) is looked up in that container.

In firebase 9.0.0, the first stable v9 release (published on npm on 25 August 2021, a week after the question was asked), the whole function is two lines (source at the firebase@9.0.0 tag):

ts
export function getFirestore(app: FirebaseApp = getApp()): Firestore {
  return _getProvider(app, 'firestore').getImmediate() as Firestore;
}

And _getProvider in @firebase/app (same tag) goes straight to the container:

ts
export function _getProvider<T extends Name>(
  app: FirebaseApp,
  name: T
): Provider<T> {
  return (app as FirebaseAppImpl).container.getProvider(name);
}

There is no check on what app actually is. With firebaseConfig as the argument, app.container is undefined, and calling .getProvider() on undefined is a plain JavaScript TypeError. The word getProvider in the message is the name of that internal method, not something in your code.

Firebase 13.0.0 has the same shape with one extra step. getFirestore() now accepts a database ID string as well, so it decides with typeof appOrDatabaseId === 'object' whether it was given an app; any object passes that test, including a config (source at firebase@13.0.0). _getProvider then reads container first to fetch the heartbeat service (source), which is why the stack trace above points at .getProvider('heartbeat') rather than at Firestore.

The asker noted that the code "works fine in CRA". The source above gives no reason for a different outcome there, and that claim was not tested here; what was tested is that the failure needs no framework at all.

Fix: create the app once and pass it#

  1. Import the app functions from firebase/app and the Firestore functions from firebase/firestore. If package.json lists only scoped @firebase/* packages, add the firebase package that provides those two entry points:
bash
npm install firebase
  1. Create the app at module level, reusing an existing one if the module is evaluated again, and pass that app to getFirestore():
js
// lib/firebase.js
import { initializeApp, getApps, getApp } from 'firebase/app';
import { getFirestore } from 'firebase/firestore';
 
const firebaseConfig = {
  apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
  authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
  projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
  appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
};
 
export const app = getApps().length ? getApp() : initializeApp(firebaseConfig);
export const db = getFirestore(app);
  1. Import db wherever you query, and pass it as the first argument of collection() or doc():
jsx
// app/cities/page.jsx
import { collection, getDocs } from 'firebase/firestore';
import { db } from '../../lib/firebase';
 
export default async function CitiesPage() {
  const snapshot = await getDocs(collection(db, 'cities'));
  return (
    <ul>
      {snapshot.docs.map((d) => (
        <li key={d.id}>{d.id}</li>
      ))}
    </ul>
  );
}

The one change that matters is the argument: the reproduction below changes nothing else between the failing and the working run.

Verify the fix#

The two scripts used for the reproduction differ by one line: getFirestore(firebaseConfig) in the first, getFirestore(app) in the second. Both create the app with initializeApp(firebaseConfig) first. On both firebase 9.0.0 and firebase 13.0.0, the working one prints the type of the returned instance and exits with code 0:

text
--- fixed.mjs
db type: firestore
exit=0

getFirestore() itself does not contact Google, so this check runs with dummy config values and no network access to Firebase. In a Next.js app, the equivalent check is that the page importing db renders instead of showing the TypeError.

If the TypeError is gone but the first query now fails with Expected first argument to collection() to be a CollectionReference, a DocumentReference or FirebaseFirestore, the database instance is not reaching collection(); the fix is covered in the collection() first-argument guide.

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.

Why the () => getFirestore(firebaseApp) workaround seemed to work#

The answer on the question (score 4, not accepted) suggests export const db = () => getFirestore(firebaseApp) and says the author does not understand the cause, guessing at a mismatch between the server and the client. The reproduction shows a simpler reason. In the question's module, firebaseApp starts life as a function, and its body reassigns the same name to the app returned by initializeApp(). What getFirestore(firebaseApp) receives therefore depends on when it runs:

  • before firebaseApp() has been called, the argument is the function itself;
  • after it has been called, the argument is a real FirebaseApp, and Firestore works.

Wrapping db in a function only delays the call until the app exists. The test script copies that pattern (a function that rebinds its own name) and calls db() once before and once after firebaseApp(). On firebase 9.0.0 and Node 14.21.3, the early call fails with the same TypeError, because a function has no container either:

text
before firebaseApp() ran: Cannot read property 'getProvider' of undefined
after firebaseApp() ran: firestore

On firebase 13.0.0 the early call fails differently. The typeof … === 'object' test described above sends a function to getApp() instead, and getApp() throws when no default app exists yet:

text
before firebaseApp() ran: Firebase: No Firebase App '[DEFAULT]' has been created - call initializeApp() first (app/no-app).
after firebaseApp() ran: firestore

So the wrapper works only as long as every caller runs after firebaseApp(), and it turns db into something each call site has to invoke. Passing the app itself, as in the fix above, removes the ordering problem instead of hiding it.

TypeScript rejects the call before it runs#

In a TypeScript project the type checker flags the bad call before anything runs. Checking a file that contains getFirestore(firebaseConfig) with tsc --noEmit, on TypeScript 7.0.2 and the firebase 13.0.0 type definitions, prints:

text
ts-check.ts(6,32): error TS2769: No overload matches this call.
  The last overload gave the following error.
    Argument of type '{ apiKey: string; projectId: string; appId: string; }' is not assignable to parameter of type 'string'.

The message names the last overload, the one that takes a database ID string, which makes it look unrelated at first glance; what it says is that a config object matches none of the four signatures listed in the reference. Plain JavaScript files, as in the question, get no such check, which is why the mistake only shows up as the run-time TypeError.

Where this leaves your Firebase module#

The rule behind this error is short: the service getters of the modular SDK take the app, never the options used to build it; only initializeApp() takes the options. In firebase 13.0.0, getAuth() (in its Node.js entry point) and getStorage() pass their argument to the same internal _getProvider() helper, so handing them the config object leads into the same missing container. Keep initializeApp() and its return value in one module, export the services built from that value, and import them everywhere else. With the wiring correct, two Firestore errors that can still follow are configuration problems rather than code ones: an emulator address that never connects, covered in the "client is offline" fix, or security rules rejecting the read, covered in the PERMISSION_DENIED guide.

Further reading: Firebase Google Sign-in on Vercel: Custom authDomain Fix.

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