firebase
semver
>=9.0.0postconditions35functions35last verified2026-06-24coverage score83%Postconditions: what we check
- signInWithEmailAndPassword · auth-errorerrorWhenAny Firebase auth failure: invalid email (auth/invalid-email), wrong password (auth/wrong-password), user not found (auth/user-not-found), user disabled (auth/user-disabled), network failure (auth/network-request-failed), or too many requests (auth/too-many-requests).Throws
FirebaseError with code property. Common codes: auth/invalid-email, auth/wrong-password, auth/user-not-found, auth/user-disabled, auth/network-request-failed, auth/too-many-requests. FirebaseError extends Error with code: string and customData: object.Required handlingCaller MUST wrap await signInWithEmailAndPassword() in try-catch. Auth failures are common in production — invalid credentials, disabled accounts. Unhandled rejection crashes the process or returns 500 to the user. try { const credential = await signInWithEmailAndPassword(auth, email, password); return credential.user; } catch (error) { if (error instanceof FirebaseError) { if (error.code === 'auth/wrong-password') throw new Error('Invalid password'); if (error.code === 'auth/user-not-found') throw new Error('User not found'); } throw error; }costhighin prodimmediate exceptionusers seelost datavisibilityvisible - createUserWithEmailAndPassword · auth-errorerrorWhenAny Firebase auth failure: email already in use (auth/email-already-in-use), invalid email (auth/invalid-email), weak password (auth/weak-password), operation not allowed (auth/operation-not-allowed), or network failure.Throws
FirebaseError with code. Common codes: auth/email-already-in-use, auth/invalid-email, auth/weak-password, auth/operation-not-allowed, auth/network-request-failed.Required handlingCaller MUST wrap await createUserWithEmailAndPassword() in try-catch. Registration failures are expected (email conflicts, weak passwords). try { const credential = await createUserWithEmailAndPassword(auth, email, password); return credential.user; } catch (error) { if (error instanceof FirebaseError) { if (error.code === 'auth/email-already-in-use') throw new Error('Email taken'); if (error.code === 'auth/weak-password') throw new Error('Password too weak'); } throw error; }costhighin prodimmediate exceptionusers seelost datavisibilityvisible - signInWithPopup · auth-errorerrorWhenAny auth failure: popup blocked (auth/popup-blocked), user cancelled (auth/popup-closed-by-user), account exists with different credential (auth/account-exists-with-different-credential), network failure, or cancelled popup before completion (auth/cancelled-popup-request).Throws
FirebaseError with code. Common codes: auth/popup-blocked, auth/popup-closed-by-user, auth/account-exists-with-different-credential, auth/cancelled-popup-request, auth/network-request-failed.Required handlingCaller MUST wrap await signInWithPopup() in try-catch. Popup blocking is common — adblockers and browser settings frequently block popups. User cancellation is also a normal flow. try { const result = await signInWithPopup(auth, provider); return result.user; } catch (error) { if (error instanceof FirebaseError && error.code === 'auth/popup-blocked') { // Fallback to redirect or inform user } throw error; }costhighin prodimmediate exceptionusers seelost datavisibilityvisible - getDocs · firestore-errorerrorWhenAny Firestore failure: permission denied (permission-denied), network failure (unavailable), resource not found (not-found), invalid query arguments (invalid-argument), or unauthenticated (unauthenticated).Throws
FirebaseError with code. Common codes: permission-denied, unavailable, not-found, invalid-argument, unauthenticated, resource-exhausted (quota).Required handlingCaller MUST wrap await getDocs() in try-catch. Permission denied errors happen when Firestore Security Rules block access. Network failures occur in offline scenarios or during Firebase outages. try { const snapshot = await getDocs(q); return snapshot.docs.map(doc => doc.data()); } catch (error) { console.error('Firestore query failed:', error.message); throw error; }costhighin prodimmediate exceptionusers seelost datavisibilityvisible - addDoc · firestore-errorerrorWhenAny Firestore failure: permission denied, invalid data (undefined values, circular references), network failure, or quota exceeded.Throws
FirebaseError with code. Common codes: permission-denied, invalid-argument, unavailable, unauthenticated, resource-exhausted.Required handlingCaller MUST wrap await addDoc() in try-catch. Permission denied is common when users are not authenticated or Firestore Security Rules block writes. try { const docRef = await addDoc(collection(db, 'items'), data); return docRef.id; } catch (error) { console.error('Failed to add document:', error.message); throw error; }costhighin prodimmediate exceptionusers seelost datavisibilityvisible - setDoc · firestore-errorerrorWhenAny Firestore failure: permission denied, invalid data, network failure, or quota exceeded.Throws
FirebaseError with code. Common codes: permission-denied, invalid-argument, unavailable, unauthenticated, resource-exhausted.Required handlingCaller MUST wrap await setDoc() in try-catch. try { await setDoc(doc(db, 'users', userId), userData); } catch (error) { console.error('Failed to set document:', error.message); throw error; }costhighin prodimmediate exceptionusers seelost datavisibilityvisible - updateDoc · firestore-errorerrorWhenAny Firestore failure: document not found (not-found), permission denied, invalid data, network failure, or unauthenticated.Throws
FirebaseError with code. Common codes: not-found, permission-denied, invalid-argument, unavailable, unauthenticated.Required handlingCaller MUST wrap await updateDoc() in try-catch. updateDoc() throws not-found if the document doesn't exist (unlike setDoc). try { await updateDoc(docRef, { lastUpdated: new Date() }); } catch (error) { console.error('Failed to update document:', error.message); throw error; }costhighin prodimmediate exceptionusers seelost datavisibilityvisible - deleteDoc · firestore-errorerrorWhenAny Firestore failure: permission denied, network failure, or unauthenticated. (Does NOT throw if document doesn't exist.)Throws
FirebaseError with code. Common codes: permission-denied, unavailable, unauthenticated, resource-exhausted.Required handlingCaller MUST wrap await deleteDoc() in try-catch. try { await deleteDoc(doc(db, 'items', itemId)); } catch (error) { console.error('Failed to delete document:', error.message); throw error; }costhighin prodimmediate exceptionusers seelost datavisibilityvisible - sendPasswordResetEmail · auth-send-password-reset-invalid-emailerrorWhensendPasswordResetEmail() called without try-catch when email is invalid format or the Auth instance is misconfigured.Throws
FirebaseError with code: auth/invalid-email (malformed email address), auth/missing-email (empty email), auth/invalid-api-key (bad Firebase config), auth/network-request-failed (network failure), auth/too-many-requests (rate limit exceeded). Note: auth/user-not-found is NOT thrown when Email Enumeration Protection is enabled (the default since 2023) — Firebase returns success silently for unknown emails.Required handlingCaller MUST wrap await sendPasswordResetEmail() in try-catch. auth/invalid-email means client-side validation should run first. auth/too-many-requests means implement rate limiting in the UI. NEVER tell users whether the email exists — return generic "if account exists, email was sent". try { await sendPasswordResetEmail(auth, email); } catch (error) { if (error instanceof FirebaseError) { if (error.code === 'auth/invalid-email') throw new Error('Invalid email format'); if (error.code === 'auth/too-many-requests') throw new Error('Too many attempts'); } throw error; }costmediumin prodimmediate exceptionusers seeauthentication failurevisibilitysilent - sendEmailVerification · auth-send-email-verification-errorerrorWhensendEmailVerification() called without try-catch.Throws
FirebaseError with code: auth/too-many-requests (sending too fast), auth/user-token-expired (user session expired — requires re-authentication), auth/network-request-failed (network failure), auth/user-disabled (account suspended), auth/invalid-user-token (user token invalid — session may be stale).Required handlingCaller MUST wrap await sendEmailVerification() in try-catch. auth/too-many-requests: Firebase limits verification email frequency — handle gracefully and inform user to wait before resending. auth/user-token-expired: User must sign in again before verification can be sent. try { await sendEmailVerification(user); } catch (error) { if (error instanceof FirebaseError) { if (error.code === 'auth/too-many-requests') { throw new Error('Verification email already sent. Please wait before resending.'); } } throw error; }costlowin prodimmediate exceptionusers seeauthentication failurevisibilitysilent - deleteUser · auth-delete-user-requires-recent-loginerrorWhendeleteUser() called without try-catch when user has not recently authenticated.Throws
FirebaseError with code: auth/requires-recent-login (user's last sign-in was too long ago — must re-authenticate before this security-sensitive operation), auth/user-not-found (user was already deleted — race condition), auth/network-request-failed (network failure), auth/user-disabled (account was suspended).Required handlingCaller MUST wrap await deleteUser() in try-catch. auth/requires-recent-login is THE most important case: prompt user to sign in again using reauthenticateWithCredential() before retrying. This is the standard Firebase security pattern for sensitive operations. try { await deleteUser(user); } catch (error) { if (error instanceof FirebaseError) { if (error.code === 'auth/requires-recent-login') { // Prompt re-authentication flow, then retry await reauthenticateWithCredential(user, credential); await deleteUser(user); } } throw error; }costhighin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - signOut · auth-sign-out-errorwarningWhensignOut() called without try-catch.Throws
FirebaseError with code: auth/network-request-failed (network failure during token revocation), auth/internal-error (internal Firebase error, rare). Note: signOut() clears local auth state even if the server request fails, so users are effectively signed out locally even on error.Required handlingCaller MUST wrap await signOut() in try-catch to prevent unhandled rejections. Network failures during signOut are rare but possible. The local session is cleared regardless, so handle gracefully (log the error, redirect to login page). try { await signOut(auth); router.push('/login'); } catch (error) { // Sign out failed on server, but local session is cleared console.error('Sign out error:', error); router.push('/login'); // Redirect regardless }costlowin prodimmediate exceptionusers seeservice unavailablevisibilitysilentSources[20] - getIdToken · auth-get-id-token-expirederrorWhengetIdToken() called without try-catch when user session is expired or revoked.Throws
FirebaseError with code: auth/user-token-expired (token expired and cannot be refreshed — user must sign in again), auth/user-disabled (account suspended while token was valid), auth/user-not-found (user deleted), auth/network-request-failed (network failure during token refresh), auth/invalid-user-token (token is malformed).Required handlingCaller MUST wrap await getIdToken() in try-catch. auth/user-token-expired is the most common case — user session invalidated (password changed, token revoked from console, user deleted). On token errors, sign the user out and redirect to login. try { const token = await getIdToken(user); // Use token in Authorization header return fetch('/api/...', { headers: { Authorization: `Bearer ${token}` } }); } catch (error) { if (error instanceof FirebaseError) { if (error.code === 'auth/user-token-expired' || error.code === 'auth/user-not-found') { await signOut(auth); router.push('/login'); } } throw error; }costhighin prodimmediate exceptionusers seeservice unavailablevisibilitysilentSources[21] - updateEmail · auth-update-email-requires-recent-loginerrorWhenupdateEmail() called without try-catch when user session is stale.Throws
FirebaseError with code: auth/requires-recent-login (must re-authenticate before changing email — security requirement), auth/email-already-in-use (email taken by another account), auth/invalid-email (malformed email), auth/operation-not-allowed (Email Enumeration Protection enabled — use verifyBeforeUpdateEmail instead), auth/network-request-failed (network failure).Required handlingCaller MUST wrap await updateEmail() in try-catch. auth/requires-recent-login is the most common error — prompt re-authentication. IMPORTANT: auth/operation-not-allowed means Email Enumeration Protection is enabled (default since 2023) — migrate to verifyBeforeUpdateEmail() which is the secure alternative. try { await updateEmail(user, newEmail); } catch (error) { if (error instanceof FirebaseError) { if (error.code === 'auth/requires-recent-login') { throw new Error('Please sign in again to change your email'); } if (error.code === 'auth/email-already-in-use') { throw new Error('Email already in use'); } } throw error; }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - updatePassword · auth-update-password-errorerrorWhenupdatePassword() called without try-catch.Throws
FirebaseError with code: auth/requires-recent-login (must re-authenticate — changing passwords is a security-sensitive operation), auth/weak-password (password too short, usually < 6 characters), auth/network-request-failed (network failure), auth/user-disabled (account suspended).Required handlingCaller MUST wrap await updatePassword() in try-catch. auth/requires-recent-login requires prompting the user to sign in again. auth/weak-password should be caught with client-side validation first. try { await updatePassword(user, newPassword); } catch (error) { if (error instanceof FirebaseError) { if (error.code === 'auth/requires-recent-login') { throw new Error('Please sign in again to change your password'); } if (error.code === 'auth/weak-password') { throw new Error('Password must be at least 6 characters'); } } throw error; }costmediumin prodimmediate exceptionusers seeauthentication failurevisibilityvisible - confirmPasswordReset · auth-confirm-password-reset-errorerrorWhenconfirmPasswordReset() called without try-catch when the reset code is invalid or expired.Throws
FirebaseError with code: auth/expired-action-code (reset link older than 1 hour — user must request a new one), auth/invalid-action-code (malformed or already-used code), auth/user-not-found (account deleted since reset was requested), auth/user-disabled (account suspended), auth/weak-password (new password too short), auth/network-request-failed (network failure).Required handlingCaller MUST wrap await confirmPasswordReset() in try-catch. auth/expired-action-code is the most common error — reset links expire after 1 hour. Prompt the user to request a new reset email. auth/invalid-action-code may mean the link was already used (single-use codes). try { await confirmPasswordReset(auth, oobCode, newPassword); } catch (error) { if (error instanceof FirebaseError) { if (error.code === 'auth/expired-action-code') { throw new Error('Reset link has expired. Please request a new one.'); } if (error.code === 'auth/invalid-action-code') { throw new Error('Reset link is invalid or has already been used.'); } } throw error; }costmediumin prodimmediate exceptionusers seeauthentication failurevisibilityvisibleSources[26] - getDoc · firestore-get-doc-errorerrorWhengetDoc() called without try-catch.Throws
FirebaseError (FirestoreError) with code: permission-denied (Firestore Security Rules block access), unavailable (network failure or Firebase outage — may resolve after retry), unauthenticated (user not signed in when rules require auth), not-found (collection or database doesn't exist — not thrown for missing docs). IMPORTANT: Returns empty DocumentSnapshot (snapshot.exists() === false) for missing documents — does NOT throw. Check snapshot.exists() before accessing snapshot.data().Required handlingCaller MUST wrap await getDoc() in try-catch AND check snapshot.exists() before access. permission-denied is very common during development (Firestore rules misconfigured). Failing to check exists() causes data() to return undefined, causing null reference errors. try { const snapshot = await getDoc(doc(db, 'users', userId)); if (!snapshot.exists()) { throw new Error('User not found'); } return snapshot.data(); } catch (error) { if (error instanceof FirebaseError) { console.error('Firestore read failed:', error.code, error.message); } throw error; }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilitysilent - runTransaction · firestore-transaction-contentionerrorWhenrunTransaction() called without try-catch when there is high write contention or the transaction function throws.Throws
FirebaseError (FirestoreError) with code: aborted (failed to commit after 5 retries due to high write contention), permission-denied (Security Rules block a read or write inside the transaction), unavailable (network failure), failed-precondition (transaction read document that no longer exists), resource-exhausted (quota exceeded), invalid-argument (more than 500 writes in one transaction). Also re-throws any error thrown inside the updateFunction.Required handlingCaller MUST wrap await runTransaction() in try-catch. aborted after 5 retries is the most critical case — indicates hot document (high contention). Consider sharding the counter or using FieldValue.increment() instead of transactions for counters. Errors thrown inside updateFunction propagate as-is — wrap with custom errors for clarity. try { const result = await runTransaction(db, async (t) => { const docSnap = await t.get(docRef); if (!docSnap.exists()) throw new Error('Document not found'); t.update(docRef, { count: docSnap.data().count + 1 }); return docSnap.data().count + 1; }); return result; } catch (error) { if (error instanceof FirebaseError && error.code === 'aborted') { throw new Error('Transaction failed after retries — high contention on document'); } throw error; }costhighin prodimmediate exceptionusers seelost datavisibilitysilent - uploadBytes · storage-upload-bytes-errorerrorWhenuploadBytes() called without try-catch.Throws
StorageError (extends FirebaseError) with code: storage/unauthorized (Security Rules block the upload — user not authenticated or path not allowed), storage/quota-exceeded (Firebase Storage quota for the project exceeded), storage/canceled (upload was cancelled), storage/retry-limit-exceeded (network too slow or unstable, max retries reached), storage/invalid-checksum (data corrupted in transit), storage/unauthenticated (no authenticated user). StorageError.code is prefixed with 'storage/'.Required handlingCaller MUST wrap await uploadBytes() in try-catch. storage/unauthorized is the most common error — check Firebase Storage Security Rules. storage/quota-exceeded requires checking Firebase project billing/quota limits. storage/retry-limit-exceeded indicates persistent network issues. try { const result = await uploadBytes(storageRef, file); return result.ref.fullPath; } catch (error) { if (error instanceof StorageError) { if (error.code === 'storage/unauthorized') { throw new Error('Not authorized to upload files'); } if (error.code === 'storage/quota-exceeded') { throw new Error('Storage quota exceeded'); } } throw error; }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - getDownloadURL · storage-get-download-url-errorerrorWhengetDownloadURL() called without try-catch.Throws
StorageError (extends FirebaseError) with code: storage/object-not-found (file was deleted or path is wrong — very common bug when referencing stale paths), storage/unauthorized (Security Rules deny read access), storage/no-download-url (file exists but has no public download URL — requires making the bucket public or using signed URLs), storage/bucket-not-found (wrong bucket name in Firebase config), storage/unauthenticated (rules require auth).Required handlingCaller MUST wrap await getDownloadURL() in try-catch. storage/object-not-found is the most common error — file was deleted, moved, or the stored path is wrong (race condition between upload and read is common). Validate file existence before serving download URLs to users. try { const url = await getDownloadURL(ref(storage, filePath)); return url; } catch (error) { if (error instanceof StorageError) { if (error.code === 'storage/object-not-found') { throw new Error('File not found'); } if (error.code === 'storage/unauthorized') { throw new Error('Not authorized to access this file'); } } throw error; }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilitysilent - signInWithEmailLink · email-link-expired-or-invaliderrorWhensignInWithEmailLink() called without try-catch. The email link has expired or was already used, or the link doesn't match the email address provided.Throws
FirebaseError with code: auth/expired-action-code (link expired — 10 minute TTL), auth/invalid-action-code (link already used or malformed), auth/invalid-email (email doesn't match the one the link was sent to), auth/user-disabled (account disabled after link was sent), auth/network-request-failed (network failure).Required handlingCaller MUST wrap await signInWithEmailLink() in try-catch. Expired and already-used links are the most common production failures — users often click links multiple times or wait too long. Each link is single-use and has a 10-minute expiration. try { const result = await signInWithEmailLink(auth, email, window.location.href); window.localStorage.removeItem('emailForSignIn'); return result.user; } catch (error) { if (error instanceof FirebaseError) { if (error.code === 'auth/expired-action-code') { throw new Error('Sign-in link has expired. Please request a new one.'); } if (error.code === 'auth/invalid-action-code') { throw new Error('Sign-in link already used. Please request a new one.'); } if (error.code === 'auth/invalid-email') { throw new Error('Email does not match the sign-in link.'); } } throw error; }costmediumin prodimmediate exceptionusers seeauthentication failurevisibilityvisible - signInAnonymously · anonymous-auth-not-enabled-or-quota-exceedederrorWhensignInAnonymously() called without try-catch. Anonymous auth not enabled in Firebase Console, or quota for anonymous accounts exceeded.Throws
FirebaseError with code: auth/operation-not-allowed (anonymous authentication not enabled in Firebase Console — frequent misconfiguration in new projects), auth/quota-exceeded (new anonymous sign-ups rate-limited, common when deployed to production without proper settings), auth/network-request-failed (network failure).Required handlingCaller MUST wrap await signInAnonymously() in try-catch. auth/operation-not-allowed means Anonymous sign-in is disabled in Firebase Console — developers often forget to enable it for new environments (staging, production). auth/quota-exceeded means Firebase is rate-limiting new anonymous sign-ups. try { const result = await signInAnonymously(auth); return result.user; } catch (error) { if (error instanceof FirebaseError) { if (error.code === 'auth/operation-not-allowed') { throw new Error('Anonymous sign-in is disabled. Enable it in Firebase Console.'); } if (error.code === 'auth/quota-exceeded') { throw new Error('Too many anonymous sign-in attempts. Please try again later.'); } } throw error; }costmediumin prodimmediate exceptionusers seeauthentication failurevisibilityvisible - updateProfile · update-profile-errorwarningWhenupdateProfile() called without try-catch. User's token expired, user was deleted, or network failure.Throws
FirebaseError with code: auth/network-request-failed (network failure during profile update — common in flaky connections), auth/user-token-expired (user's auth token has expired, requires re-authentication), auth/user-not-found (user account was deleted between sign-in and the profile update call).Required handlingCaller MUST wrap await updateProfile() in try-catch. auth/user-token-expired requires the user to sign in again. This function is commonly called immediately after createUserWithEmailAndPassword() in onboarding flows — both should be wrapped together. try { await updateProfile(auth.currentUser, { displayName: name, photoURL: avatarUrl }); } catch (error) { if (error instanceof FirebaseError) { if (error.code === 'auth/network-request-failed') { throw new Error('Network error updating profile. Please try again.'); } if (error.code === 'auth/user-token-expired') { throw new Error('Session expired. Please sign in again.'); } } throw error; }costlowin prodimmediate exceptionusers seelost datavisibilitysilent - linkWithCredential · link-credential-conflicterrorWhenlinkWithCredential() called without try-catch. The credential is already associated with another Firebase account, or an account with that email already exists.Throws
FirebaseError with code: auth/credential-already-in-use (the credential is already linked to a different Firebase user account — cannot link the same Google/GitHub account to multiple Firebase users), auth/account-exists-with-different-credential (an account with the same email already exists but uses a different sign-in method — e.g. user has email/password account and tries to link Google with same email), auth/email-already-in-use (for email/password credential linking when email is taken), auth/provider-already-linked (user already has this provider linked), auth/requires-recent-login (security-sensitive operation requires recent sign-in).Required handlingCaller MUST wrap await linkWithCredential() in try-catch. auth/credential-already-in-use is the most important case — the linked credential's user needs to be merged or the conflict resolved. auth/account-exists-with-different-credential is very common in social auth flows where users try to link an OAuth account that matches an existing email/password account. try { const result = await linkWithCredential(user, credential); return result.user; } catch (error) { if (error instanceof FirebaseError) { if (error.code === 'auth/credential-already-in-use') { throw new Error('This account is already linked to another user.'); } if (error.code === 'auth/account-exists-with-different-credential') { throw new Error('An account with this email already exists with a different sign-in method.'); } if (error.code === 'auth/requires-recent-login') { throw new Error('Please sign in again before linking accounts.'); } } throw error; }costmediumin prodimmediate exceptionusers seeauthentication failurevisibilityvisible - reauthenticateWithCredential · reauth-credential-errorerrorWhenreauthenticateWithCredential() called without try-catch. Wrong password, stale credential, or user mismatch.Throws
FirebaseError with code: auth/wrong-password (incorrect password for email/password credential — user mistyped current password), auth/user-mismatch (credential does not correspond to the currently signed-in user — wrong account), auth/invalid-credential (the auth credential is malformed or has expired — OAuth token expired), auth/user-not-found (user account was deleted), auth/network-request-failed (network failure during re-auth).Required handlingCaller MUST wrap await reauthenticateWithCredential() in try-catch. This is required before deleteUser() or updateEmail() — if it fails, the subsequent sensitive operation must not proceed. auth/wrong-password is the most common failure. auth/user-mismatch means the credentials don't belong to the current user. const credential = EmailAuthProvider.credential(email, currentPassword); try { await reauthenticateWithCredential(auth.currentUser, credential); // Now safe to call deleteUser() or updateEmail() } catch (error) { if (error instanceof FirebaseError) { if (error.code === 'auth/wrong-password') { throw new Error('Current password is incorrect.'); } if (error.code === 'auth/user-mismatch') { throw new Error('Credential does not match the current user.'); } } throw error; }costmediumin prodimmediate exceptionusers seeauthentication failurevisibilityvisible - verifyBeforeUpdateEmail · verify-before-update-email-errorerrorWhenverifyBeforeUpdateEmail() called without try-catch. Email already in use, requires recent login, or network failure.Throws
FirebaseError with code: auth/email-already-in-use (new email already registered to another account), auth/requires-recent-login (security-sensitive operation — user must have signed in within the last few minutes), auth/invalid-email (new email address is malformed), auth/network-request-failed (network failure during verification email send), auth/operation-not-allowed (email/password accounts not enabled — unlikely but possible in Identity Platform configs).Required handlingCaller MUST wrap await verifyBeforeUpdateEmail() in try-catch. auth/requires-recent-login is the most common failure — the user must reauthenticate before changing their email. auth/email-already-in-use means the new email is taken. try { await verifyBeforeUpdateEmail(auth.currentUser, newEmail); // Tell user to check their new email inbox } catch (error) { if (error instanceof FirebaseError) { if (error.code === 'auth/email-already-in-use') { throw new Error('This email address is already registered.'); } if (error.code === 'auth/requires-recent-login') { throw new Error('Please sign in again before changing your email.'); } } throw error; }costlowin prodimmediate exceptionusers seeauthentication failurevisibilitysilent - writeBatch.commit · batch-commit-permission-deniederrorWhenbatch.commit() called without try-catch. Firestore Security Rules deny one or more writes in the batch, or the batch exceeds 500 operations.Throws
FirebaseError (FirestoreError) with code: permission-denied (Security Rules block one or more write operations in the batch — the entire batch fails atomically), not-found (a batch.update() or batch.delete() references a document that does not exist), resource-exhausted (batch exceeds 500 operations or rate limits), unavailable (Firestore temporarily unavailable — transient network/service issue), unauthenticated (user not authenticated and rules require auth). The WriteBatch.commit() Promise will never resolve while offline.Required handlingCaller MUST wrap await batch.commit() in try-catch. A single permission-denied write in the batch fails ALL writes atomically. Check that all document paths in the batch pass Security Rules before committing. Monitor for resource-exhausted if batches dynamically grow with user-generated content. const batch = writeBatch(db); batch.set(doc(db, 'users', userId), userData); batch.update(doc(db, 'counters', 'users'), { count: increment(1) }); try { await batch.commit(); } catch (error) { if (error instanceof FirebaseError) { if (error.code === 'permission-denied') { throw new Error('Not authorized to perform this update.'); } if (error.code === 'resource-exhausted') { throw new Error('Batch too large or rate limit exceeded. Reduce batch size.'); } } throw error; }costhighin prodimmediate exceptionusers seelost datavisibilitysilent - getCountFromServer · count-from-server-errorwarningWhengetCountFromServer() called without try-catch. Firestore Security Rules deny the query, or the underlying index is not ready.Throws
FirebaseError (FirestoreError) with code: permission-denied (Security Rules deny the underlying query the count is based on — common when the query has compound filters that require field-level auth), unauthenticated (user not authenticated and Security Rules require auth), failed-precondition (composite index not yet built for the query — very common during development when adding new queries), resource-exhausted (project quota exceeded or too many reads in short window), unavailable (Firestore temporarily unavailable).Required handlingCaller MUST wrap await getCountFromServer() in try-catch. failed-precondition with a message about missing indexes is very common during development — Firestore will include the index creation URL in the error message. permission-denied occurs when Security Rules don't allow the underlying collection read. try { const snapshot = await getCountFromServer(query(collection(db, 'orders'), where('userId', '==', userId))); return snapshot.data().count; } catch (error) { if (error instanceof FirebaseError) { if (error.code === 'permission-denied') { throw new Error('Not authorized to count this collection.'); } if (error.code === 'failed-precondition') { throw new Error('Missing Firestore index. Check Firebase Console.'); } } throw error; }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilitysilent - deleteObject · delete-object-errorerrorWhendeleteObject() called without try-catch. File does not exist, or Security Rules deny the delete operation.Throws
StorageError (extends FirebaseError) with code: storage/object-not-found (file was already deleted or path is wrong — concurrent deletion is common in user-facing apps where users can delete from multiple sessions), storage/unauthorized (Security Rules deny the delete — user does not own the file or rules require ownership check), storage/unauthenticated (user is not authenticated but rules require auth), storage/retry-limit-exceeded (network failure with all retries exhausted).Required handlingCaller MUST wrap await deleteObject() in try-catch. storage/object-not-found should be treated as a success (idempotent delete is safe) or at minimum should not crash the user-facing operation. Always check Security Rules: rules must verify file ownership to prevent users deleting others' files. try { await deleteObject(ref(storage, filePath)); } catch (error) { if (error instanceof StorageError) { if (error.code === 'storage/object-not-found') { // File already deleted — treat as success for idempotent delete return; } if (error.code === 'storage/unauthorized') { throw new Error('Not authorized to delete this file.'); } } throw error; }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilitysilent - httpsCallable · functions-call-unauthenticated-or-permission-deniederrorWhenhttpsCallable(functions, name) result called without try-catch. User not authenticated, permission denied, function not found, or internal server error.Throws
FunctionsError (extends FirebaseError) with code: functions/unauthenticated (function requires Firebase Authentication and user is not signed in), functions/permission-denied (user is signed in but does not have permission to call this function — typically enforced via Firebase Auth claims check in the function), functions/not-found (function does not exist or was not deployed — common during development), functions/internal (unhandled exception in the Cloud Function — function threw an error not wrapped in HttpsError), functions/unavailable (Cloud Functions temporarily unavailable — transient), functions/deadline-exceeded (function execution timeout — 60s default), functions/resource-exhausted (quota exceeded — max concurrent calls reached).Required handlingCaller MUST wrap await fn(data) in try-catch, NOT await httpsCallable() itself. functions/unauthenticated means the user must sign in before calling the function. functions/internal means the Cloud Function itself crashed — check server logs. functions/permission-denied is an application-level authorization failure. Use error.code to distinguish user-facing errors from internal server errors. const sendInvite = httpsCallable(functions, 'sendInvite'); try { const result = await sendInvite({ email, organizationId }); return result.data; } catch (error) { if (error instanceof FunctionsError) { if (error.code === 'functions/unauthenticated') { throw new Error('Please sign in to perform this action.'); } if (error.code === 'functions/permission-denied') { throw new Error('You do not have permission to perform this action.'); } if (error.code === 'functions/internal') { throw new Error('Server error. Please try again later.'); } } throw error; }costhighin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - applyActionCode · apply-action-code-errorerrorWhenapplyActionCode(auth, oobCode) called without try-catch. Action code is expired, invalid (malformed or already used), or the associated user has been disabled or deleted.Throws
FirebaseError with code property. Common codes: auth/expired-action-code (code has passed its TTL — Firebase action codes expire after ~1 hour by default), auth/invalid-action-code (code is malformed, used a second time, or revoked), auth/user-disabled (the user account associated with the code was disabled by an admin), auth/user-not-found (the user account associated with the code was deleted between email send and link click).Required handlingCaller MUST wrap await applyActionCode() in try-catch. Action codes commonly fail in production — users click email links hours later, paste codes into the wrong tab, or retry already-applied codes. Unhandled rejection breaks the verification flow and leaves the user staring at a generic error page. try { await applyActionCode(auth, oobCode); return { verified: true }; } catch (error) { if (error instanceof FirebaseError) { if (error.code === 'auth/expired-action-code') { return { error: 'This verification link has expired. Please request a new one.' }; } if (error.code === 'auth/invalid-action-code') { return { error: 'This verification link is no longer valid. It may have already been used.' }; } } throw error; }costmediumin prodimmediate exceptionusers seeauthentication failurevisibilityvisible - getRedirectResult · redirect-result-errorerrorWhengetRedirectResult(auth) called without try-catch on the post-redirect page. OAuth provider rejected the sign-in, an account already exists with the same email under a different provider, or the credential is already linked to another user.Throws
FirebaseError with code property. Common codes: auth/account-exists-with-different-credential (the email returned by the provider is already linked to another sign-in method — common when a user signs up with Google then tries to sign in with Apple using the same email), auth/credential-already-in-use (linkWithRedirect: this provider credential is already linked to a different Firebase user — must merge or unlink first), auth/web-storage-unsupported (browser blocks sessionStorage — third-party cookies disabled, Safari ITP, incognito mode), auth/operation-not-supported-in-this-environment (called from a non-browser environment), auth/network-request-failed (network error during token exchange), auth/internal-error (Firebase Auth backend issue).Required handlingCaller MUST wrap await getRedirectResult() in try-catch. This is the FIRST thing that runs after a successful OAuth redirect — an unhandled rejection here means the user lands on a broken page with no recovery path. Web storage errors are especially common in Safari and incognito sessions. try { const result = await getRedirectResult(auth); if (result) { return { user: result.user }; } return { user: null }; } catch (error) { if (error instanceof FirebaseError) { if (error.code === 'auth/account-exists-with-different-credential') { return { error: 'An account with this email exists. Sign in with your original provider.' }; } if (error.code === 'auth/web-storage-unsupported') { return { error: 'Your browser blocks the storage needed to sign in. Try a different browser or enable third-party cookies.' }; } } throw error; }costhighin prodimmediate exceptionusers seeauthentication failurevisibilityvisible - signInWithCustomToken · custom-token-errorerrorWhensignInWithCustomToken(auth, token) called without try-catch. Custom token is malformed, expired, signed by a different Firebase project (wrong audience), or the user account was disabled.Throws
FirebaseError with code property. Common codes: auth/invalid-custom-token (token is not a valid Firebase custom token — usually missing claims, wrong format, or expired), auth/custom-token-mismatch (token was signed for a different Firebase project — common when staging tokens leak into production or vice versa), auth/user-disabled (the target uid is disabled in the Firebase console), auth/network-request-failed (network issue during token verification).Required handlingCaller MUST wrap await signInWithCustomToken() in try-catch. Custom token sign-in is the bridge between a backend auth system and Firebase — failures here lock users out of every Firebase feature (Firestore, Storage, Functions). Token-mismatch errors are especially insidious because they only surface in production when env vars get swapped. try { const credential = await signInWithCustomToken(auth, customToken); return { user: credential.user }; } catch (error) { if (error instanceof FirebaseError) { if (error.code === 'auth/invalid-custom-token' || error.code === 'auth/custom-token-mismatch') { console.error('Custom token rejected — check backend token minting:', error.code); throw new Error('Authentication failed. Please contact support.'); } } throw error; }costhighin prodimmediate exceptionusers seeauthentication failurevisibilityvisible - uploadString · upload-string-errorerrorWhenuploadString(ref, value, format) called without try-catch. Storage security rules deny the upload, the bucket quota is exhausted, the network drops mid-upload, or the string format is invalid for the declared format type.Throws
StorageError (extends FirebaseError) with code property. Common codes: storage/unauthorized (security rules deny write — common when uid in path doesn't match auth.uid), storage/quota-exceeded (bucket free-tier quota exhausted), storage/retry-limit-exceeded (network failed all retries — slow connection or backend issue), storage/unauthorized-app (App Check failure — common during development), storage/invalid-format (string isn't valid for the declared format, e.g. malformed base64 or data URL with wrong MIME).Required handlingCaller MUST wrap await uploadString() in try-catch. Upload failures must be distinguished from success — silently treating a rejection as success leaves downstream features (avatar display, document export) referencing nonexistent objects. try { const snapshot = await uploadString(ref, dataUrl, 'data_url'); return { path: snapshot.ref.fullPath }; } catch (error) { if (error instanceof StorageError) { if (error.code === 'storage/unauthorized') { throw new Error('You do not have permission to upload here.'); } if (error.code === 'storage/quota-exceeded') { throw new Error('Storage quota exceeded. Please contact support.'); } } throw error; }costmediumin prodimmediate exceptionusers seelost datavisibilityvisible - getMetadata · get-metadata-errorerrorWhengetMetadata(ref) called without try-catch. Object does not exist at the referenced path, the user lacks read permission per Storage rules, or the network request fails.Throws
StorageError (extends FirebaseError) with code property. Common codes: storage/object-not-found (no object at this path — common when checking for optional artifacts like profile photos before display), storage/unauthorized (Storage rules deny read access — typical when the path encodes a uid different from auth.uid), storage/retry-limit-exceeded (network failure exhausted retries), storage/unauthorized-app (App Check rejected the request).Required handlingCaller MUST wrap await getMetadata() in try-catch. storage/object-not-found is the expected path for "does this file exist?" probes — swallowing rejections silently can mask permission errors as missing-file errors and vice versa, leading to mysteriously broken features. try { const metadata = await getMetadata(ref); return metadata; } catch (error) { if (error instanceof StorageError) { if (error.code === 'storage/object-not-found') { return null; } if (error.code === 'storage/unauthorized') { throw new Error('You do not have permission to view this file.'); } } throw error; }costlowin prodimmediate exceptionusers seedegraded performancevisibilityvisible
Sources
Every postcondition cites at least one of these. Grouped by source type; numbered to match the footnotes above.
Official documentation
- [1]firebase.google.com/docs/reference/jsAuth
- [2]firebase.google.com/docs/auth/webPassword Auth
- [3]firebase.google.com/docs/reference/jsAuth
- [4]firebase.google.com/docs/reference/jsAuth
- [5]firebase.google.com/docs/auth/webGoogle Signin
- [6]firebase.google.com/docs/reference/jsFirestore
- [7]firebase.google.com/docs/firestore/query-dataGet Data
- [8]firebase.google.com/docs/reference/jsFirestore
- [9]firebase.google.com/docs/firestore/manage-dataAdd Data
- [10]firebase.google.com/docs/reference/jsFirestore
- [11]firebase.google.com/docs/reference/jsFirestore
- [12]firebase.google.com/docs/reference/jsFirestore
- [13]firebase.google.com/docs/firestore/manage-dataDelete Data
- [14]firebase.google.com/docs/reference/jsAuth
- [15]firebase.google.com/docs/auth/webManage Users
- [16]firebase.google.com/docs/reference/jsAuth
- [17]firebase.google.com/docs/auth/webManage Users
- [18]firebase.google.com/docs/reference/jsAuth
- [19]firebase.google.com/docs/auth/webManage Users
- [20]firebase.google.com/docs/reference/jsAuth
- [21]firebase.google.com/docs/reference/jsAuth
- [22]firebase.google.com/docs/reference/jsAuth
- [23]firebase.google.com/docs/auth/webManage Users
- [24]firebase.google.com/docs/reference/jsAuth
- [25]firebase.google.com/docs/auth/webManage Users
- [26]firebase.google.com/docs/reference/jsAuth
- [27]firebase.google.com/docs/reference/jsFirestore
- [28]firebase.google.com/docs/reference/jsFirestore
- [29]firebase.google.com/docs/firestore/manage-dataTransactions
- [30]firebase.google.com/docs/reference/jsStorage
- [31]firebase.google.com/docs/storage/webUpload Files
- [32]firebase.google.com/docs/reference/jsStorage
- [33]firebase.google.com/docs/storage/webDownload Files
- [34]firebase.google.com/docs/auth/webEmail Link Auth
- [35]firebase.google.com/docs/reference/jsAuth
- [36]firebase.google.com/docs/auth/webAnonymous Auth
- [37]firebase.google.com/docs/reference/jsAuth
- [38]firebase.google.com/docs/reference/jsAuth
- [39]firebase.google.com/docs/auth/webManage Users
- [40]firebase.google.com/docs/reference/jsAuth
- [41]firebase.google.com/docs/auth/webAccount Linking
- [42]firebase.google.com/docs/reference/jsAuth
- [43]firebase.google.com/docs/auth/webManage Users
- [44]firebase.google.com/docs/reference/jsAuth
- [45]firebase.google.com/docs/auth/webManage Users
- [46]firebase.google.com/docs/reference/jsFirestore .Writebatch
- [47]firebase.google.com/docs/firestore/manage-dataTransactions
- [48]firebase.google.com/docs/reference/jsFirestore
- [49]firebase.google.com/docs/firestore/query-dataAggregation Queries
- [50]firebase.google.com/docs/reference/jsStorage
- [51]firebase.google.com/docs/storage/webDelete Files
- [52]firebase.google.com/docs/functions/callableCallable
- [53]firebase.google.com/docs/reference/jsFunctions
- [54]firebase.google.com/docs/reference/jsAuth
- [55]firebase.google.com/docs/reference/jsAuth
- [56]firebase.google.com/docs/auth/webRedirect Best Practices
- [57]firebase.google.com/docs/reference/jsAuth
- [58]firebase.google.com/docs/auth/adminCreate Custom Tokens
- [59]firebase.google.com/docs/reference/jsStorage
- [60]firebase.google.com/docs/storage/webHandle Errors
- [61]firebase.google.com/docs/reference/jsStorage
- [62]firebase.google.com/docs/storage/webFile Metadata
Research notes
Curator notes from SOURCES.md captured when the profile was written so you can verify the reasoning, not just the rules.
Firebase — Sources
Behavioral Claims
All functions can throw FirebaseError on network failure, permission denied, invalid credentials, or service errors. This is documented in the official Firebase JavaScript SDK reference.
Official Documentation
Error Handling
- https://firebase.google.com/docs/reference/js/auth.md#signinwithemailandpassword — signInWithEmailAndPassword throws with FirebaseError codes
- https://firebase.google.com/docs/reference/js/auth.md#createuserwithemailandpassword — createUserWithEmailAndPassword throws on weak password, email in use
- https://firebase.google.com/docs/reference/js/auth.md#signinwithpopup — signInWithPopup throws when popup blocked, user cancels
- https://firebase.google.com/docs/reference/js/firestore_.md#getdocs — getDocs throws on permission denied, network error
- https://firebase.google.com/docs/reference/js/firestore_.md#adddoc — addDoc throws on permission denied, invalid data
- https://firebase.google.com/docs/reference/js/firestore_.md#setdoc — setDoc throws on permission denied, invalid data
- https://firebase.google.com/docs/reference/js/firestore_.md#updatedoc — updateDoc throws on not-found, permission denied
- https://firebase.google.com/docs/reference/js/firestore_.md#deletedoc — deleteDoc throws on permission denied, network error
Error Codes and Types
- https://firebase.google.com/docs/auth/admin/errors — Firebase Auth error codes (auth/wrong-password, auth/user-not-found, etc.)
- https://firebase.google.com/docs/firestore/query-data/get-data — Firestore read error handling
- https://firebase.google.com/docs/firestore/manage-data/add-data — Firestore write error handling
Error Type Documentation
- https://firebase.google.com/docs/reference/js/app.firebaseerror.md — FirebaseError class (extends Error, has .code property)
Real-World Evidence
Correct Usage (0 violations)
- namviek (2.4k★) —
apps/frontend/libs/firebase.ts:signInWithPopupwrapped in.then().catch()pattern - git-friend (44★) —
context/auth-context.tsx:signInWithPopupandsignOutproperly wrapped in try-catch
Known Violation Pattern (manual confirmation)
- venice (~500★) —
connectors/connector-copilot/CopilotClient.ts:35-52:signInWithEmailAndPassword(auth, email, password).then(async (creds) => {...})— no.catch()on the outer call. This is a TRUE POSITIVE violation.
Package Versions Observed in Production
- 9.6.3 (hackernews-react-graphql)
- 9.8.1 (venice)
- 10.7.1 (namviek)
- 10.11.0 (Memex)
- 10.12.1 (penrose)
- 11.1.0 (upscayl)
- 11.3.1 (akveo/nebular)
- 12.7.0 (git-friend)
Notes
- This contract covers the modular API (v9+) only. The v8 compat API (firebase/compat) uses different method signatures (namespace API like
firebase.auth()) and is not covered. - Functions are imported from sub-module paths (
firebase/auth,firebase/firestore) which the analyzer normalizes to thefirebasepackage. deleteDoc()does NOT throw if the document doesn't exist — it succeeds silently.getAuth(),getFirestore(),collection(),doc(),query(),where()are synchronous helpers and do NOT require try-catch.
Need a different package?
Request a profile