Open TimmNL opened 4 months ago
I couldn't figure out how to label this issue, so I've labeled it for a human to triage. Hang tight.
@jbalidiong Do you have a status update on this? Do you maybe know if someone is having a look into this?
Hi @TimmNL ,
Just want to double check, if removing force token refresh, the multi tab is working?
Could you please provide a log so that we can see what happened before internal assertion failed?
It would also be helpful if you can have a constant repo way since it is hard to debug if the only way triggering it is waiting for a couple of days.
The team thinks what is going on here might relate to two issues.
The firebase app broken due to the uncaught exception throw from AsyncQueue, that's why checking what is the last calling function is important.
Firestore has build in AuthToken refresh so refreshing on App developer site is unnecessary.
Could you please share more of "breaks local persistence over multiple tabs"?
It is referring to, for example you have one tab open and it listen from cache, the another tab is modifying the cache but the first tab didn't get update?
Hi @cherylEnkidu,
Thanks for the response!
You are correct; removing the following code snippet did indeed make it work in multiple tabs:
onAuthStateChanged(auth, async (user) => {
if (import.meta.env.MODE === 'development') {
// eslint-disable-next-line no-console -- only show on dev
console.log('user state changed', user?.uid);
}
await user?.getIdToken(true);
if (import.meta.env.MODE === 'development') {
// eslint-disable-next-line no-console -- only show on dev
console.log('user state changed - getIdToken called', user?.uid);
}
});
By "breaks local persistence over multiple tabs," I mean that after opening a second tab with the same site, the first tab starts throwing the same error as shown above. The newly opened tab, however, works fine and doesn't show any problems.
I've executed the following steps, this time running with my emulator:
open the website in a first tab:
Wait for 5-10 minutes (if earlier, it doesn't throw the error)
Open up the website in a second tab while keeping the first tab opened.
Check the console of the first tab again:
When I check the emulator logs, it states that some requests have come in with request.auth having the value null.
The reason for adding this was to ensure the user existed when executing a query, as occasionally, after having the app open for a couple of days, it would show that auth.currentUser.uid was null. My idea was that requesting a new token with user?.getIdToken(true) when the auth state changed would fetch a new token for the user, resolving the issue. However, the problem persists, and users still do not receive any data after a few days.
@cherylEnkidu or @jbalidiong, Do you have an update on this? I'm still stuck on it and I'm getting more complains from users about it. Thanks!
@cherylEnkidu and @jbalidiong , Today I got a different error message while working on a hosted version of the project. I got the following, in which the user is authorized but I get Target ID already exists: 4
.
I'm not sure what this means, but I added the screenshot of it.
Hopefully this helps is some way
Hi @TimmNL ,
The team receives some bug report ticket related to exception throw from AsyncQueue. We are working on investigation to see whether they are related to the same root cause. In the meantime while I am working on reproducing this issue, could you please provide the stack trace of the crash?
@cherylEnkidu,
Here is a stack-trace, if I get a different outcome in another place, I'll add that trace as wel:
@cherylEnkidu Is the strack-trace I sent of any help? Do you have any update on what could be going on here and how I could fix it? Or might you have a work-around for the time being? Thanks!
@cherylEnkidu,
I recently set up Rollbar to get better visibility on the bugs in my project. I just got this stack trace:
Hopefully, it helps in tracking down the issue!
@cherylEnkidu or @jbalidiong, Do you have any updates on the issue and do the stack-traces provide any information?
Hi @TimmNL ,
Thank you for providing the stack trace. The problem you are running into could be related to some under investigation crashes inside AsyncQueue
. Here is a relevant ticket and its content might be able to inspire you: https://github.com/firebase/firebase-js-sdk/issues/8250
While investigating, could you please provide a full debug log just like the developer provided inside the other ticket?
As I posted in #8250 the solution in my case was to downgrade to 10.6.0
, after a lot of testing I found tha on 10.7.0
is where the issue starts. Hope it helps
Thanks for the comments, I haven't had the time to continue on this since I was on holiday.
I've tried downgrading to 10.6.0
, but this gives problems with the persistence, since users have used newer versions of the app. I'm getting the following error:
@firebase/firestore: Firestore (10.6.0): Error using user provided cache. Falling back to memory cache: FirebaseError: [code=failed-precondition]: A newer version of the Firestore SDK was previously used and so the persisted data is not compatible with the version of the SDK you are now using. The SDK will operate with persistence disabled. If you need persistence, please re-upgrade to a newer version of the SDK or else clear the persisted IndexedDB data for your app to start fresh.
@ElianCordoba have you had the same problem? If so, how did you fix this? I don't mind clearing the indexedDb, but I'm not sure yet on how I can get the sdk version used to save the local data so I can compare the versions and clear the indexedDb if needed..
@cherylEnkidu With the release of 11.0.0
yesterday I tried to remove all the extra logic I added to make firestore work and upgrade to that version, but this gives problems straight out of the box, as I get the following errors when opening a new tab. To be clear, I did remove handleVisibilityChange
and onAuthStateChanged
to make sure firestore handles everything itself.
So my code now looks as followed:
initializing firebase auth:
import {
Auth,
browserLocalPersistence,
connectAuthEmulator,
getAuth,
setPersistence,
} from 'firebase/auth';
import { initAppCheck } from './app-check';
import { initApp } from './init-app';
let auth: Auth | undefined;
export const getAuthentication = async () => {
if (!auth) {
import.meta.env.MODE !== 'development' && initAppCheck(initApp());
auth = getAuth(initApp());
if (
import.meta.env.MODE === 'development' &&
import.meta.env.VITE_FIREBASE_EMULATOR_URL
) {
connectAuthEmulator(
auth,
`http://${import.meta.env.VITE_FIREBASE_EMULATOR_URL}:9099`,
);
}
await setPersistence(auth, browserLocalPersistence);
}
return auth;
};
export const clearAuth = () => {
auth = undefined;
};
initializing firestore database
import {
connectFirestoreEmulator,
Firestore,
initializeFirestore,
persistentLocalCache,
persistentMultipleTabManager,
} from 'firebase/firestore';
import { getAnalytics } from './get-analytics';
import { getPerformanceMonitoring } from './get-performance-monitoring';
import { initApp } from './init-app';
let database: Firestore | undefined;
export const getDatabase = () => {
if (!database) {
database = initializeFirestore(initApp(), {
localCache: persistentLocalCache(
/*settings*/ { tabManager: persistentMultipleTabManager() },
),
});
if (
import.meta.env.MODE === 'development' &&
import.meta.env.VITE_FIREBASE_EMULATOR_URL
) {
connectFirestoreEmulator(
database,
import.meta.env.VITE_FIREBASE_EMULATOR_URL,
8080,
);
}
getAnalytics(initApp());
getPerformanceMonitoring(initApp());
}
return database;
};
export const clearDatabase = () => {
database = undefined;
};
@ElianCordoba I've tried to downgrade and remove the cache, but sadly when opening a second tab I still get the same error. Have you implemented the persistentLocalCache
? And if so, have you implemented Firestore in a different way or done anything different if you compare your implementation to the implementation I shared before?
@cherylEnkidu, @jbalidiong or @DellaBitta, after further debugging, it seems the issue might be related to the line await setPersistence(auth, browserLocalPersistence);
. When I remove or change it to browserSessionPersistence
, the errors don't appear immediately. However, since we need authentication persistence for our users, turning it off isn’t a viable solution for us.
When persistence is disabled, I can log into a new tab (while keeping the original tab open) with the same account, and everything works as expected. All changes reflect correctly, and from what I can tell, it seems like the data is being pulled from the same IndexedDb instance. However, it's difficult to confirm for sure.
What I noticed is that when a second tab is opened, the user entry is removed from the local storage. My assumption is that when a second tab is opened, this tab clears the localStorage user until this user is verified. The original tab however is triggered by this and wants to get the changed user information. If the second tab isn't done confirming the user information, there is no user for the original tab and this means that the new user on the original tab is set to null
.
This is very inconsistent and what I noticed is that by reloading the second tab a couple of times it eventually gets triggered.
I switched the log level to debug to gather more details, and I noticed the following.
Based on the location of the fail
function and nearby comments, I traced it to this file in your repo: https://github.com/firebase/firebase-js-sdk/blob/4db3d3e7be8b435b523d23b0910958a495c09ad8/packages/firestore/src/util/async_queue_impl.ts#L239
Somehow, in the minified version, it doesn't have the AsyncQueue is already failed:
as parameter.
Another odd thing I noticed in the logs is that when I open a new tab, the current user is set to null and shortly after to an anonymous-user. This triggers an attempt to refetch all the queries, which are then rejected with the permission-denied error.
The last errors in the logs point to this line: https://github.com/firebase/firebase-js-sdk/blob/4db3d3e7be8b435b523d23b0910958a495c09ad8/packages/firestore/src/local/local_store_impl.ts#L1041.
The comment above this function suggests that it shouldn’t be executed when multi-tab support is active, but in my case, the error is pointing directly to it.
Given that the logs could contain sensitive information about our app, how can I share them with you securely, if needed?
This issue has been open for a while now, and it's causing increasing problems the longer it remains unresolved. Any updates or guidance would be greatly appreciated.
@TimmNL this may be very silly, but can you try calling await disableNetwork(db);
before calling await enableNetwork(db);
?
I had a very similar issue while using persistentLocalCache
, persistentMultipleTabManager
, enableNetwork
and disableNetwork
. My use case is creating an app with online sync only for paid users. The error appeared only for paid users, when I tried to enable the network. Disabling the network before enabling it consistently solved this error for me.
Edit: from the docs:
"enableNetwork(firestore) Re-enables use of the network for this Firestore instance after a prior call to disableNetwork()."
Weird though that the error is the unexpected state instead of a more friendly reminder.
@Daquisu Thank you for your assistance and efforts in finding a solution!
Could you please clarify your suggestion? Are you recommending that I disable the network immediately after initializing the app, and then re-enable it? Or, are you suggesting that the network should be disabled when the user unfocuses the app, as implied in the original comment where I have the Firestore database initialization logic?
In my situation, the app attempts to sync from the moment it starts until the user goes offline. The primary issue I'm facing is that the Firestore connection can spontaneously terminate while the app is open on the user's machine. Additionally, if the user opens a second tab, the Firestore connection in the first tab is disconnected.
These issues occur even if the user never goes offline, disrupting the connection between Firestore and the client. have you experienced problems like this as well?
@cherylEnkidu, I recently came across the indexedDBLocalPersistence option for firebase-auth, but I couldn't find any official documentation for it. Could you confirm if this is a maintained and recommended option? In my initial testing, it seems to effectively address the multi-tab synchronization issue. Any additional insights or documentation you could share would be greatly appreciated.
@TimmNL do you have insights in the size of your IndexedDB database once you see this INTERNAL ASSERTION
-failures? Curious if you are dealing with the same issues as i just described in this ticket:
https://github.com/firebase/firebase-js-sdk/issues/8250#issuecomment-2475837002
Hi @basvandorst,
I recently encountered an instance of the internal-assertion failure when multiple tabs were open. It seems my previous fix didn't resolve the issue as expected. However, my IndexedDB size is relatively small, so I believe this might not be the same root cause as the one you're experiencing.
Here's a screenshot for reference:
Let me know if additional details or logs might help narrow this down!
Operating System
MacOS 14.5
Browser Version
Safari, Chrome, Firefox
Firebase SDK Version
10.12.4
Firebase SDK Product:
AppCheck, Auth, Firestore, Functions
Describe your project's tooling
React with Typescript, Tanstack Query, Vitest and Vite
Describe the problem
I'm trying to create a web-app with offline support, in which the logged in user can create, update and delete todo's. Currently I have implemented firebase auth, firestore, firebase appCheck and firebase functions to trigger some cloud functions when the user is online.
The problem I'm having is that users complain that when they have the web-app opened for a couple of days, they eventually get the following error:
INTERNAL ASSERTION FAILED: Unexpected state.
When the user reloads the page, all data is fetched again and everything works fine.
After a little research I found that sometimes the user isn't logged in anymore (firebase/auth's currentUser is null), until they reload the app. It doesn't matter if the user is online or offline, the reload logs the user back in.
To solve this I added some extra code to my auth-handler, which resulted in not being able to share the state between different tabs of the same browser. I'd like to have that functionality back and also a fix of some sort for users that have lost the connection to the app until they reload the app.
Steps and code to reproduce issue
In my app I use singeltons for the app, database and auth to make sure they stay alive, even when the user isn't actively using them. The calls are all done through tanstack query.
My initialization of the firestore app looks like this:
initialization of firebase auth:
initializing firestore database:
Creating a query
Tanstack query:
Place where the error comes shows up:
As seen in the code above, I make use of the
onSnapshot
function to get all the updates.I wonder if I'm the only one with this issue, I could not find an issue related to mine.
Thanks for the help!