Coding Diary: Why my Firestore didn’t works?

Chonnarong Hanyawongse
7 min readMar 20, 2019

--

https://freebiesupply.com/logos/react-native-firebase-logo/

I decided to write this as a diary because I was facing very weird error which I can’t find any useful information on the internet. I was stuck on this error for long and it was really annoying.

What I’m going to share here is how I debugged and dig into 3rd party codes. Hopefully this will helps saving the time to debug when something like this happens to anyone…

Technologies Stack

Although this is not really related, but it might helps bringing people who searching for the solutions here.

I use React Native with Firebase as the backend. And I also use react-native-firebase (https://rnfirebase.io/) as a JavaScript layer to connect to the native SDKs. The database, of cause I choose Cloud Firestore instead of Realtime Database (event it was originally in Beta) because of the following reason:

Cloud Firestore is Firebase’s new flagship database for mobile app development. It improves on the successes of the Realtime Database with a new, more intuitive data model. Cloud Firestore also features richer, faster queries and scales better than the Realtime Database.
(https://firebase.google.com/docs/database/rtdb-vs-firestore)

And yes I agreed with it.

So, what’s the problem?

Okay, during development, I noticed the first issue: Why the collection I get is not always refreshed with the recent data on server?

Well, this is not always happen…

If I did:

await firebase.auth().signOut();

And then:

await firebase.auth().signInWithCredential(credential);

It will starts to work normally. But after some times at some circumstance, it broke again and again.

So… I suspected that something might be wrong with the cache mechanism. At that time, it convinced me to assume that Cloud Firestore might still having bug on this (I started to use it at the time it was still in beta…)

With above assumption, I then put the below code to force getting data from server regardless if we had the cache or not. It was not what I wanted because we actually wanted user to also be able to use app offline… Anyway, I did it like this as workaround.

await ref.get({ source: 'server' });

And yes, it was working. But then there are other problems.

Weird firestore/unavailable exception

Now after the workaround, during development, something odd still happens. I got firestore/unavailable error permanently when did above call after some circumstance…

What!? It was working…

Without thinking, I did signOut and signIn again, and it solves the problem. So this means to me that it had something to do with the authentication token. Not the cache bug I suspected originally.

Before doing anything else, I used the phase “firestore/unavailable” to search for relevant topics, and I found nothing really related to the issue I had. But at least I found the key that this error code is own by react-native-firebase. And it indicates that “device is not able to connect to the service for some reason” (https://rnfirebase.io/docs/v5.x.x/firestore/reference/FirestoreError).

But how come…? I can see that my simulator device connected to the internet and I can see other firebase service just works. Also, it contradicted because the issue could get resolved with signOut and signIn again.

So I suspected that it might be react-native-firebase bug. Because the error message was there. I started to seriously find the relevant discussions, patch and workaround about it. Unfortunately, I didn’t find anything helpful.

So then, I decided to look at the code my own. I opened react-native-firebase code and tried to locate where this messages get thrown. And finally found these parts of code:

/ios/RNFirebase/firestore/RNFirebaseFirestore.m
/android/src/main/java/io/invertase/firebase/firestore/RNFirebaseFirestore.java

This means that the error message created from native code part of react-native-firebase. If I have to debug, I will need to use either Android Studio or Xcode to debug it. I chose Xcode because I’m on Mac. iOS simulator is way faster for me.

First Clue…

Since the error message is created by react-native-firebase, I assumed that there could be actual error behind that. So I started to debug using Xcode. And found that there is error code 14 as picture below:

Error code 14: Failed to get documents from server (RNFirebaseFirestoreCollectionReference.m)

This convinced me to think that there should be something that blocks me from reaching out to the resources. And I suspected that it should be something about authentication token because it started to work once signOut and re-signIn.

To confirm it was token issue, I started the timer after did signIn. And yes, it will not working properly after 1 hour exactly.

May be token had expired after an hour?

I went through Firebase auth document and found…

Firebase ID tokens are short lived and last for an hour; the refresh token can be used to retrieve new ID tokens. Refresh tokens expire only when one of the following occurs: The user is deleted. The user is disabled.
(https://firebase.google.com/docs/auth/admin/manage-sessions)

Okay, may be my token was not refreshed. I then googled for proper way to refresh the token and found the interesting answer from StackOverflow as below:

Firebase automatically refreshes if it needs to. For example if you are using real time database or Firestore, they will automatically refresh the token after it expires since they require a persistent connection and an ID token for that. This will cause that listener to trigger.
(https://stackoverflow.com/a/49697718)

So what was going on with my token refresh mechanism???

Second Clue…

Okay, if it was not refresh automatically, I will force refresh it. To force refresh the token, I used the following method:

await firebase.auth().currentUser.getIdToken(true);

If I call this method immediately after signIn, then I will get the new token back. But when I called it after ID token expired, I got the following error:

An internal error has occurred, please try again.

This error told nothing and it usually hide something inside. So I try to debug the method using Xcode and found the interesting error.

Error code 17999 with none of useful information

The error has no description and seems to be unknown. That’s why the end error message looks like that.

This almost concluded that there were something broken at the time refreshing token. It didn’t seems to be react-native-firebase bug anymore.

So the reason it was not working could be explained like this:

  1. Once signIn, new ID token generated
  2. After an hour, ID token expired
  3. When we call Firestore to get collection, it saw that the token expired, so it attempted to refresh the token
  4. The token couldn’t be refreshed due to the unknown error above
  5. Firestore don’t know what to do, so it throw unknown error and react-native-firebase translated it to firestore/unavailable exception

Okay, we need to get deeper to see what was going on exactly.

The root cause

To see what happen to the refresh token, we need to dig into the code where it communicate with firebase server. And I found it under FIRAuthBackend.m (FirebaseAuth project). There is the method which is doing that communication.

FirebaseAuth/FIRAuthBackend.m at method asyncPostToURLWithRequestConfiguration

When I put breakpoint to see what responded from server after request was made, I then got very interesting message. It said:

Token Service API has not been used in project xxxyyyzzz before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/securetoken.googleapis.com/overview?project=xxxyyyzzz then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.

And then I follow the link. And this is what I saw…

Google APIs console showing Token Service API is already enabled.

The API told me that I didn’t enable it. But this service was actually enabled as it shown on the web console. I guess something might be broken at the time initiate this Firebase project.

Finally, I just disable and re-enable it. And it works 😂🎉🎉🎉

There were no component packages bug. It was just something wrong with my Token Service API account…

Conclusion

At the end, it just a simple fix. But I would never know what was a root cause if I didn’t debug into it.

What I have learned from this is not to biasing blame the 3rd package before having actual evidence. If I debug first, I would not lost days for finding if someone having similar trouble as me just to get the patch, workaround or pending fix. This time the root cause was from somewhere unforeseen. So debugging is the best way to find the right answer.

Thank you for reading! Hope this helps anyone who having similar trouble 😊

--

--