I’m trying to use the emulator only when it is available. connectAuthEmulator
does not fail if the emulator is not available (i.e.: if I haven’t ran firebase emulators:start
). It fails later when I try to make a request.
To do this, I’m fetching the emulator URL (http://localhost:9099
) using fetch
. If the request succeeds, then I call connectAuthEmulator
. Otherwise, I do nothing (uses the configured cloud service).
I have a problem where the fetch
works, but connectAuthEmulator
throws an error: auth/emulator-config-failed
. For some weird reason, this seem to happen only when I’m logged in and I make no requests for about 15 seconds. If I spam requests however, the error never occurs.
import { initializeApp } from "firebase/app";
import { getAuth, connectAuthEmulator } from "firebase/auth";
const app = initializeApp({ /** ... */ });
const auth = getAuth(app);
if (NODE_ENV === "development") {
(async () => {
try {
const authEmulatorUrl = "http://localhost:9099";
await fetch(authEmulatorUrl);
connectAuthEmulator(auth, authEmulatorUrl, {
disableWarnings: true,
});
console.info("🎮 Firebase Auth: emulated");
} catch (e) {
console.info("🔥 Firebase Auth: not emulated");
}
})()
}
export { auth };
Any idea why this happens and how to fix it?
3
Answers
Solution #1
First, try
http://127.0.0.1:9099
instead ofhttp://localhost:9099
(don't forget to set this as your emulatorhost
infirebase.json
).Solution #2
On top of using solution #1, try rendering your app after everything Firebase-related has init. You can do this by creating listeners on the status of your emulator connections.
src/config/firebase.ts
src/index.tsx
Solution #3
Forcibly change the value of
auth._canInitEmulator
((auth as unknown as any)._canInitEmulator
for TS) totrue
. This can have some unexpected side-effects (see answer), because some requests might go to your cloud before the emulator kicks in. This can be mitigated with solution #2 (which should prevent requests from firing)Full Answer
Part of the issue here is that I was performing
connectAuthEmulator
in an async function, when the documentation clearly states it should be called immediately (and synchronously) aftergetAuth
. I was aware of this, but I did not see any other alternative to the issue of connecting to the emulator only when it is available.I dug into the source code for connectAuthEmulator (permalink) and found out that the error is thrown here:
authInternal._canInitEmulator
was false when the error occurred. There is only one place where this property is set to false, in_performFetchWithErrorHandling
, here (permalink). This is because it's the first time it performs an API request for the Auth service. So I concluded that Firebase disallows the use of emulators after the first request usingauth
is made. This is probably to prevent making some requests on your cloud, then switching over to an emulator. So my call toconnectAuthEmulator
was probably done after a request was already made. I couldn't figure out where though. Even with my app not being loaded (solution #2), the error would still occur.My conclusion was right, as I later found this
error.ts
file which pretty much says this:This whole investigation could've been done earlier if
FirebaseError
had shown this message instead of justauth/emulator-config-failed
. It is visible if youconsole.log(error.message)
, but I was not aware of that at the time.For some reason, using the localhost IP instead fixed it instantly and I never had the error again. I added the solution #2 and #3 as another measure after.
try http://127.0.0.1:9099 instead of http://localhost:9099 (don’t forget to set this as your emulator host in firebase.json).
I think the root cause of this problem comes from
React.StrictMode
. This mode will re-render twice time. It might have been initiated twice time too. I am pretty sure because when I disabled theReact.StrictMode
and refreshed the browser many times. The error does not show up anymore.