Home > Software design >  "Failed to get document because the client is offline" Firebase error with the firestore e
"Failed to get document because the client is offline" Firebase error with the firestore e

Time:12-16

EDIT: I know it is something with the emulators as it works fine without them.

I am following a course for Next js and i am using the Firebase emulator (it recommends to do so but doesn't have a tutorial to do so as its very easy) and I am trying to read some data from firestore for a user, but it always gets the error, Failed to get document because the client is offline. The auth emulator works fine but the firestore one doesn't. Even if I was disconnected from the internet (which i am not as I am writing this question and can visit any website) idk why it would have that error as the website is being hosted on next js. Here is the custom hook I made in the course to connect to firebase:

import { initializeApp } from "firebase/app";
import {
    getAuth,
    signInWithPopup,
    signOut,
    signInAnonymously,
    GoogleAuthProvider,
    connectAuthEmulator,
} from "firebase/auth";
import { getFirestore, doc, onSnapshot, connectFirestoreEmulator, getDoc } from "firebase/firestore";

const firebaseConfig = {
    apiKey: "MY APIKEY",
    authDomain: "My AUTH DOMAIN",
    projectId: "nextfirE (in the course i am using we are making are making a blog type app called nextfire)",
    storageBucket: "STOREAGEBUKET DOMAIN",
    messagingSenderId: "messagingSenderId",
    appId: "aPP id",
    measurementId: "measurementId",
};

const firebase = initializeApp(firebaseConfig);
const auth = getAuth();
const db = getFirestore()
export {
    GoogleAuthProvider,
    auth,
    signInWithPopup,
    signOut,
    signInAnonymously,
    db,
    doc,
    onSnapshot,
    getDoc
};

if (window.location.hostname === "localhost") {
    connectAuthEmulator(auth, "http://localhost:9099");
    connectFirestoreEmulator(db, "https://localhost", 8080)
}

This is for a username form, here is the react component that handles the form:

function Username() {
    const [formValue, setFormValue] = useState("");
    const [isValid, setIsValid] = useState(false);
    const [loading, setLoading] = useState(false);

    const { user, username } = useContext(UserContext);

    useEffect(() => {
        checkUsername(formValue);
    }, [formValue]);

    const onChange = (e) => {
        const val = e.target.value.toLowerCase();
        const regex = /^(?=[a-zA-Z0-9._]{3,15}$)(?!.*[_.]{2})[^_.].*[^_.]$/;

        if (val.length < 3) {
            setFormValue(val);
            setLoading(false);
            setIsValid(false);
        }

        if (regex.test(val)) {
            setFormValue(val);
            setLoading(true);
            setIsValid(false);
        }
    };

    const checkUsername = useCallback(
        debounce(async (username: string) => {
            if (username.length >= 3) {
                const ref = doc(db, `usernames/${username}`);
                const docSnap = await getDoc(ref);

                if (docSnap.exists()) {
                    setIsValid(true);
                    setLoading(false);
                } else {
                    setIsValid(false);
                    setLoading(false);
                }
            }
        }, 500),
        []
    );

    return (
        !username && (
            <section>
                <h3>Choose Your Username</h3>
                <form>
                    <input
                        name="username"
                        placeholder="username"
                        value={formValue}
                        onChange={onChange}
                    />
                    <button
                        type="submit"
                        className="btn-green"
                        disabled={!isValid}
                    >
                        Take Username
                    </button>

                    <h3>Debug State</h3>
                    <div>
                        Username: {formValue}
                        <br />
                        Loading: {loading.toString()}
                        <br />
                        Username Valid: {isValid.toString()}
                    </div>
                </form>
            </section>
        )
    );
}

It has some regex checks and length checks but if it passes those goes to firestore, when I enter in a username that is already taken (if it has been taken there would be a firestore document that exist with the username as the name of the doc) it takes a few seconds to show an error, but when i put in a valid username that has not been taken it gives my a error instantly. Can you please help me?

Thanks

CodePudding user response:

Thanks to Gourav B I was able to find the answer. I just had to remove the https:// in front of localhost in the connectFirestoreEmulator. I guess in the connectFirestoreEmulator they connect to https:// host port (the host and port are the second and third parameter). Thanks Gourav B!

CodePudding user response:

The Failed to get document because the client is offline

error occurs when you attempt to retrieve a single document, you are offline, and the requested document is absent from the local cache. In this case, the client has no information about the document and does not know if it exists on the backend. There is no way to know if the document exists until a network connection is re-established. So to avoid incorrectly reporting that a document does not exist, this error is produced.

This situation can largely be avoided by getting the document while in an online state so that it is present in the local cache. Note, however, that the local cache is a cache, and "stale" entries may be evicted from the cache to make room for other data. If the data in the document does not change frequently, you could also instead register a snapshot listener via addSnapshotListener() and keep the listener alive to ensure that the document remains in the local cache. Also, there can be multiple other reasons for this error, some them are :

  1. You tried to reference the collection/ document which was not yet created. So, just create a collection with a dummy doc or just wait for the document to be created and see if it works fine.
  2. Implement try ... catch around all get().await() calls. Unhandled exceptions can lead to this error
  3. Issue was fixed by changing the port number of the emulator. Try for 8081 if 8080 is not working,

Also as per the Firebase documentation and GitHub link change,

if (window.location.hostname === "localhost") {
 connectAuthEmulator(auth, "http://localhost:9099");
 connectFirestoreEmulator(db, "https://localhost", 8080) }

to

if (window.location.hostname === "localhost") { 
connectAuthEmulator(auth, "http://localhost:9099"); 
connectFirestoreEmulator(db, "localhost", 8080) }
  1. Update all packages and enable offline persistence for your web app like this :

    firebase.firestore().enablePersistence()

https://firebase.google.com/docs/firestore/manage-data/enable-offline

[5] This may also happen because OnCompleteListener is triggered when the task is completed, either it fails or succeeds. To avoid the error, you can use separate OnSuccessListener and OnFailureListener OnSuccessListener is called when the task is succeeded, but if the above error occurs, OnFailureListener will be triggered and you can handle the error in onFailure() method of the listener.

[6] Check for correct Project ID when passing Project ID to initializeApp or setting environment variable.

admin.initializeApp({ projectId: "your-project-id" });

Or

export GCLOUD_PROJECT="your-project-id"

If still facing issues, try following this documentation to connect your app to Firestore Emulator and I'd also recommend enabling debug logging to see if it gives a hint as to why the client can't connect.

  • Related