Supabase

useEmulator disableWarnings: TS2554 Expected 1 arguments

Reproduced on firebase 8.10.1, 9.23.0 and 13.0.0: which declaration file rejects the options object, what disableWarnings really silences in each version, and the two fixes that compile.

Mahdi Ben RhoumaMahdi Ben Rhouma
8 min read

TL;DR#

Passing { disableWarnings: true } to firebase.auth().useEmulator() fails to compile because the namespaced firebase typings declare useEmulator(url: string): void, with one parameter. The JavaScript underneath already accepted the options object in 8.2.10, the version in the report, and so does the separate @firebase/auth-types package, but firebase.auth() is typed from the declaration file inside the firebase package itself. The issue (firebase/firebase-js-sdk#4591) was closed with "only fixed in 9.0+", and that holds for the modular connectAuthEmulator() only: the compat declaration file still has the one-parameter signature in firebase 13.0.0. Fix: switch the emulator call to connectAuthEmulator(auth, url, { disableWarnings: true }), or, if the codebase must stay on firebase/compat or v8, cast firebase.auth() to a small local type that declares the second parameter.

The compiler output, reproduced with TypeScript 5.9.3 against firebase 8.10.1 (the last v8 release), 9.23.0 compat and 13.0.0 compat, on 21 October 2026 in the node:22-alpine Docker image:

text
index.ts(5,54): error TS2554: Expected 1 arguments, but got 2.

The issue's screenshot shows the same error as a VS Code tooltip, "Expected 1 arguments, but got 2. ts(2554)", on a useEmulator('http://localhost:9099', { disableWarnings: true }) call. A minimal file that triggers it:

ts
import firebase from 'firebase/app';
import 'firebase/auth';
 
firebase.initializeApp({ apiKey: 'demo-key', projectId: 'demo-test' });
firebase.auth().useEmulator('http://127.0.0.1:9099', { disableWarnings: true });

Why it happens#

Firebase ships its auth types in two places, and the pull request that added the option only touched one of them.

PR #4119, merged on 3 December 2020, added the banner and the option to what was then the experimental TypeScript Auth SDK. Its diff changes packages-exp/auth-types-exp/index.d.ts to useEmulator(url: string, options?: { disableWarnings: boolean }): void. That package became the modular API of v9. The v8 runtime got the option as well: in the 8.10.1 source of the legacy auth package, useEmulator(url, options) reads options['disableWarnings'] and passes it to the warning function. The @firebase/auth-types declaration at the same tag has the second parameter too.

But import firebase from 'firebase/app' does not use @firebase/auth-types. The firebase/app entry point's package.json sets "typings": "../index.d.ts", which is the hand-maintained namespace file of the firebase package. In that file, at the 8.10.1 tag, firebase.auth.Auth still declares useEmulator(url: string): void. The reporter later traced it on 8.2.10: the types come from packages/firebase/index.d.ts, "And not from the @firebase/auth-types". This is also why their first suspicion, that the nested @firebase folder in node_modules was out of date, led nowhere. The nested package was already correct, even at the 8.2.10 tag; it is just not the one the compiler reads.

When v9 moved the namespaced API to firebase/compat, the namespace file moved with it, signature unchanged. The compat declaration at the 13.0.0 tag still declares one parameter, while the compat runtime at the same tag accepts the options and forwards them to connectAuthEmulator(). The maintainer's closing comment, "The typing is correct in v9 and we won't be patching v8", is accurate for the modular function; the reproduction below shows the compat path still fails on 13.0.0.

What each install did in the reproduction:

text
$ npm ls firebase @firebase/auth @firebase/auth-types typescript (v8 project)
r@ /work/v8
+-- firebase@8.10.1
| `-- @firebase/auth@0.16.8
|   `-- @firebase/auth-types@0.10.3
`-- typescript@5.9.3

The same index.ts with firebase/compat/app and firebase/compat/auth imports gave the identical TS2554 line on firebase 9.23.0 and on 13.0.0. The modular version compiled with exit=0 on 13.0.0.

Fix#

1. Use the modular connectAuthEmulator()#

This is the only form whose published type includes the options object. The source at 13.0.0 declares connectAuthEmulator(auth: Auth, url: string, options?: { disableWarnings: boolean }), and the Auth emulator connection guide uses the same function.

ts
import { initializeApp } from 'firebase/app';
import { getAuth, connectAuthEmulator } from 'firebase/auth';
 
const app = initializeApp({ apiKey: 'demo-key', projectId: 'demo-test' });
const auth = getAuth(app);
connectAuthEmulator(auth, 'http://127.0.0.1:9099', { disableWarnings: true });

Call it right after getAuth(), before any sign-in call. Once Auth has started making requests, a first call, or a call with a different configuration, fails the assertions in the same file with the auth/emulator-config-failed code; repeating an identical call is a no-op.

2. On v8 or firebase/compat, cast to a local type#

If the code has to keep firebase.auth(), declare the signature the runtime actually accepts and cast to it. No extra package is needed:

ts
import firebase from 'firebase/app';
import 'firebase/auth';
 
type AuthWithEmulatorOptions = {
  useEmulator(url: string, options?: { disableWarnings?: boolean }): void;
};
 
firebase.initializeApp({ apiKey: 'demo-key', projectId: 'demo-test' });
const auth = firebase.auth() as unknown as AuthWithEmulatorOptions;
auth.useEmulator('http://127.0.0.1:9099', { disableWarnings: true });

For compat, only the two imports change, to firebase/compat/app and firebase/compat/auth. This file compiled with exit=0 on 8.10.1, on 9.23.0 compat and on 13.0.0 compat. Keep the cast on the one line that needs it: the rest of the code should keep using the real firebase.auth.Auth type.

A cast to FirebaseAuth imported from @firebase/auth-types also compiled on 8.10.1. It works because that declaration has the right signature, but it imports a package the project does not list as a dependency, which only resolves while npm hoists it. The local type avoids that.

One approach that looked reasonable did not work in the reproduction. This specific declare module 'firebase/app' augmentation, which adds the two-parameter overload to auth.Auth left the TS2554 error in place on 8.10.1, unchanged. A // @ts-ignore above the call would silence the error, but it would also hide any real mistake on that line, such as a misspelled option name.

Verify the fix#

Run the compiler in no-emit mode on the file that connects the emulator:

bash
npx tsc --noEmit -p .

The TS2554 line should be gone. Then start the emulator with firebase emulators:start --only auth and load the app. With disableWarnings: true on firebase 13.0.0, the WARNING: You are using the Auth Emulator line should no longer appear in the browser console; on v8 and on the first v9 release it still appears (see the next section). To check the banner, run this in the console; it should return null:

js
document.querySelector('.firebase-emulator-warning')

The firebase-emulator-warning class name is taken from the SDK source. The banner itself was not observed in this reproduction, which ran in Node, where there is no DOM.

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.

Older releases keep the console line with disableWarnings#

Even with the types sorted, the option does not do the same thing in every version, and that changes what "it worked" looks like.

In the 8.10.1 runtime, the warning function always calls consoleInfo(), and disableWarnings only skips the fixed-position banner appended to document.body. Running the same call in Node with the option set to false and then to true printed the line both times:

text
$ node runtime.js true
--- v8 useEmulator, disableWarnings: true
WARNING: You are using the Auth Emulator, which is intended for local testing only.  Do not use with production credentials.
--- end

The first modular release kept that split: in the source of the first v9 release, console.info() runs unconditionally and only the banner checks the flag. On 13.0.0, the modular code skips the whole warning function when the option is set, console line included. The control run, with false, printed the line; the run with true printed nothing between the markers:

text
$ node runtime.mjs true
--- connectAuthEmulator, disableWarnings: true
--- end

The compat useEmulator() on 13.0.0 behaved the same way, since it delegates to that function. So a v8 or early v9 app that still logs the warning is behaving as its source says, not ignoring the option. The doc comment on connectAuthEmulator() only mentions "the warning banner attached to the DOM", so the console behaviour is not documented; it is read from the code. The same block also skips the warning for hosts that the SDK detects as Cloud Workstations, whatever the option says.

Once the emulator call compiles, the next failure people tend to hit in the same setup is auth/invalid-api-key, thrown by getAuth() before the emulator is even contacted when the config has no apiKey; that check and its fix are covered here. If Firestore is wired to the emulator in the same file, two related pitfalls are worth checking: a scheme passed as the host, which makes reads fail with "Failed to get document because the client is offline", and a module that runs twice under Next.js, which produces "Host has been set in both settings() and useEmulator()".

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