Error Medic

Firebase Rate Limit Exceeded & Auth Errors: 401, 403, 502, 503, Token Expired Fix

Fix Firebase rate limit, 401 unauthorized, 403 forbidden, 502/503 errors, and token expiry issues with step-by-step diagnostic commands and proven solutions.

Last updated:
Last verified:
2,314 words
Key Takeaways
  • Firebase 429/rate-limited errors occur when your app exceeds per-minute or daily quota limits on Firestore, Realtime Database, Authentication, or Cloud Functions — check the Firebase console Usage & Billing tab first
  • 401 Unauthorized and 'invalid token' errors almost always mean an expired or malformed ID token; Firebase ID tokens expire after 1 hour and must be refreshed via currentUser.getIdToken(true) before retrying
  • 403 Forbidden errors indicate a Security Rules denial — not an auth failure — so audit your Firestore/Storage rules in the Firebase console Rules Playground before assuming token issues
  • 502 Bad Gateway and 503 Service Unavailable are upstream Firebase infrastructure errors; implement exponential backoff with jitter (start at 1s, cap at 32s) and check status.firebase.google.com before escalating
  • Firebase connection refused locally almost always means the Emulator Suite is not running or is bound to the wrong port — run 'firebase emulators:start' and verify FIREBASE_AUTH_EMULATOR_HOST is set
Fix Approaches Compared
MethodWhen to UseTime to ImplementRisk
Force token refresh (getIdToken(true))401 / token expired on valid user session< 5 minutesLow — standard SDK call
Exponential backoff retry wrapper429 rate limited, 502, 503 transient errors30–60 minutesLow — purely additive
Firestore Security Rules audit + fix403 forbidden on known-authenticated user15–45 minutesMedium — wrong rules lock out all users
Increase Firebase quota via GCP consolePersistent 429 on production traffic spike1–2 hours (approval)Low — cost increase only
Switch to Firebase App Check401/403 from unauthenticated/bot abuse2–4 hoursMedium — requires client SDK update
Enable offline persistence + retry queueIntermittent 503/timeout on mobile clients1–3 hoursLow — improves resilience
Migrate hot collection to sharded countersSustained rate limit on single Firestore doc4–8 hoursMedium — data model change

Understanding Firebase Rate Limit and Authentication Errors

Firebase is a suite of backend services, each with its own quota system and error semantics. When developers encounter errors like Firebase: Error (auth/network-request-failed), HTTP 429, or PERMISSION_DENIED, the root cause is rarely obvious because the same symptom can arise from several distinct failure modes.

This guide walks through every major error class — rate limiting, authentication failures, authorization denials, and infrastructure errors — with concrete diagnostic steps and fixes.


Error Taxonomy: What Each Status Code Means in Firebase

HTTP Code Firebase Context Common Cause
401 Auth REST API, custom backend middleware Expired or missing ID token
403 Firestore, Storage, RTDB Security Rules Rules evaluation returned false
429 Auth (sign-up), Firestore writes, Functions Per-minute or daily quota exceeded
502 Firebase Hosting, Functions, Auth Upstream Google infrastructure error
503 Firestore, Functions cold start overload Service temporarily unavailable

Step 1: Identify Which Service Is Failing

Before writing a single line of fix code, determine exactly which Firebase service is returning the error.

In the browser DevTools Network tab, filter by firestore.googleapis.com, identitytoolkit.googleapis.com, or cloudfunctions.net and inspect the failing request. Note the exact HTTP status and response body — Firebase error responses always include a JSON body like:

{
  "error": {
    "code": 429,
    "message": "Quota exceeded for quota metric 'datastore.googleapis.com/api/request_count'",
    "status": "RESOURCE_EXHAUSTED"
  }
}

For server-side Node.js apps, enable verbose logging:

process.env.FIREBASE_ADMIN_LOG_LEVEL = 'debug';

Check the Firebase console: Navigate to Project Settings → Usage and billing → and inspect per-service quota gauges. The Firestore dashboard shows read/write/delete operations per day.


Step 2: Fix Firebase 401 — Authentication Failed / Unauthorized

A 401 from Firebase almost always means the ID token in your request Authorization header is expired, malformed, or belongs to a deleted user.

Firebase ID tokens expire after exactly 1 hour. If your app does not proactively refresh tokens, users who keep a tab open will start getting 401s after an hour of activity.

Diagnosis: Decode the token at jwt.io and check the exp field. If Date.now()/1000 > exp, the token is expired.

Fix — force refresh before critical calls:

async function getValidToken(user) {
  // Pass true to force-refresh even if not yet expired
  const token = await user.getIdToken(/* forceRefresh= */ true);
  return token;
}

Fix — set up a proactive refresh listener:

import { getAuth, onIdTokenChanged } from 'firebase/auth';

const auth = getAuth();
let cachedToken = null;

onIdTokenChanged(auth, async (user) => {
  if (user) {
    cachedToken = await user.getIdToken();
    // Reschedule refresh before the 1-hour expiry
    setTimeout(async () => {
      cachedToken = await user.getIdToken(true);
    }, 55 * 60 * 1000); // 55 minutes
  } else {
    cachedToken = null;
  }
});

Common 401 error messages you will see:

  • Firebase: Error (auth/id-token-expired).
  • Firebase ID token has expired. Get a fresh token from your client app and try again.
  • Request had invalid authentication credentials.

Step 3: Fix Firebase 403 — Permission Denied / Forbidden

A 403 from Firestore or Storage means your Security Rules denied the operation — the user IS authenticated, but the rules say they cannot perform this action.

This is the most misdiagnosed Firebase error. Developers assume the token is invalid, but the token is fine; the rules are wrong.

Diagnosis using the Rules Playground:

  1. Open Firebase console → Firestore → Rules → Rules Playground
  2. Set the request path, method (get/list/create/update/delete), and paste a valid user UID
  3. Click Run — it will show exactly which rule line denied the request

Example broken rule:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{userId} {
      // Bug: uses request.auth.uid but unauthenticated users hit this path too
      allow read: if request.auth.uid == userId;
      allow write: if request.auth.uid == userId;
    }
  }
}

Fixed rule with null check:

allow read, write: if request.auth != null && request.auth.uid == userId;

Common 403 error messages:

  • FirebaseError: [code=permission-denied]: Missing or insufficient permissions.
  • Firebase Storage: User does not have permission to access 'gs://...' (storage/unauthorized).

Step 4: Fix Firebase Rate Limited / 429 Errors

Firebase Authentication limits sign-up and password reset emails to prevent abuse. Firestore has per-project write quotas (1 write/second per document on the free Spark plan).

Auth rate limiting — you will see:

  • Firebase: We have blocked all requests from this device due to unusual activity. Try again later. (auth/too-many-requests).

This is per-IP and resets automatically. During development, disable it temporarily:

firebase auth:block [--disable]

In production, implement CAPTCHA or App Check to prevent bots from triggering this.

Firestore write rate limiting — single documents cannot be written more than ~1 time per second sustained. For counters or leaderboards, use distributed/sharded counters:

// Instead of one counter doc, shard across N documents
const SHARD_COUNT = 10;
const shardId = Math.floor(Math.random() * SHARD_COUNT).toString();
const shardRef = db.collection('counters').doc(counterName)
  .collection('shards').doc(shardId);
await shardRef.set({ count: increment(1) }, { merge: true });

Quota increases: For RESOURCE_EXHAUSTED errors on the Blaze plan, open GCP console → APIs & Services → Quotas → search firestore and request an increase.


Step 5: Fix Firebase 502 / 503 — Bad Gateway and Service Unavailable

These are transient infrastructure errors. Do not retry immediately — this worsens the cascade. Implement exponential backoff:

async function retryWithBackoff(fn, maxRetries = 5) {
  let attempt = 0;
  while (attempt < maxRetries) {
    try {
      return await fn();
    } catch (err) {
      const isRetryable = [429, 500, 502, 503, 504].includes(err?.code) ||
        err?.message?.includes('unavailable');
      if (!isRetryable || attempt === maxRetries - 1) throw err;
      const delay = Math.min(1000 * 2 ** attempt + Math.random() * 500, 32000);
      await new Promise(r => setTimeout(r, delay));
      attempt++;
    }
  }
}

Always check https://status.firebase.google.com before debugging your own code for these errors.


Step 6: Fix Firebase Connection Refused

In local development with the Emulator Suite, connection refused on ports 8080 (Firestore), 9099 (Auth), or 5001 (Functions) means the emulator is not running or your app is not pointed at it.

# Start all emulators defined in firebase.json
firebase emulators:start

# Verify ports are listening
ss -tlnp | grep -E '8080|9099|5001'

Ensure your app connects to the emulators:

if (process.env.NODE_ENV === 'development') {
  connectFirestoreEmulator(db, 'localhost', 8080);
  connectAuthEmulator(auth, 'http://localhost:9099');
}

Frequently Asked Questions

bash
#!/usr/bin/env bash
# Firebase Diagnostic Script
# Run from your project root with: bash firebase-diag.sh

set -euo pipefail
PROJECT=$(firebase use 2>/dev/null | grep -oP '(?<=Now using project )\S+' || echo 'UNKNOWN')
echo "=== Firebase Diagnostics for project: $PROJECT ==="

# 1. Check Firebase CLI version
echo "\n--- Firebase CLI version ---"
firebase --version

# 2. Verify authentication
echo "\n--- Authenticated account ---"
firebase login:list

# 3. Check quota usage via GCP (requires gcloud CLI)
echo "\n--- Firestore quota usage (last 1 hour) ---"
if command -v gcloud &>/dev/null; then
  gcloud monitoring metrics list \
    --filter='metric.type="firestore.googleapis.com/api/request_count"' \
    --project="$PROJECT" 2>/dev/null | head -20 || echo 'gcloud metrics unavailable'
else
  echo 'gcloud CLI not installed — install from https://cloud.google.com/sdk'
fi

# 4. Test Firebase Auth REST endpoint reachability
echo "\n--- Auth endpoint connectivity ---"
curl -sf -o /dev/null -w "HTTP %{http_code} in %{time_total}s\n" \
  "https://identitytoolkit.googleapis.com/v1/projects" || \
  echo 'FAILED: Cannot reach identitytoolkit.googleapis.com'

# 5. Test Firestore endpoint reachability
echo "\n--- Firestore endpoint connectivity ---"
curl -sf -o /dev/null -w "HTTP %{http_code} in %{time_total}s\n" \
  "https://firestore.googleapis.com/$discovery/rest?version=v1" || \
  echo 'FAILED: Cannot reach firestore.googleapis.com'

# 6. Check Firebase status page
echo "\n--- Firebase platform status ---"
STATUS=$(curl -sf https://status.firebase.google.com/api/v2/summary.json | \
  python3 -c "import sys,json; d=json.load(sys.stdin); \
  [print(c['name'],'-',c['status']) for c in d.get('components',[]) if c.get('status') != 'operational']" \
  2>/dev/null || echo 'Could not fetch status')
echo "${STATUS:-All Firebase components operational}"

# 7. Decode a JWT token (pass as first argument)
if [[ "${1:-}" =~ ^eyJ ]]; then
  echo "\n--- Decoding provided JWT ---"
  PAYLOAD=$(echo "$1" | cut -d. -f2 | base64 -d 2>/dev/null || echo '{}')
  echo "$PAYLOAD" | python3 -m json.tool
  EXP=$(echo "$PAYLOAD" | python3 -c "import sys,json; print(json.load(sys.stdin).get('exp',0))" 2>/dev/null)
  NOW=$(date +%s)
  if (( EXP > 0 && NOW > EXP )); then
    echo "TOKEN EXPIRED at $(date -d @$EXP)  ($(( (NOW - EXP) / 60 )) minutes ago)"
  elif (( EXP > 0 )); then
    echo "Token valid until $(date -d @$EXP)  ($(( (EXP - NOW) / 60 )) minutes remaining)"
  fi
fi

# 8. Check emulator ports (development only)
echo "\n--- Emulator port check ---"
for PORT in 4000 5001 8080 9099 9199; do
  if ss -tlnp 2>/dev/null | grep -q ":$PORT "; then
    echo "Port $PORT: LISTENING"
  else
    echo "Port $PORT: not in use"
  fi
done

echo "\n=== Diagnostics complete ==="
E

Error Medic Editorial

The Error Medic Editorial team consists of senior DevOps engineers and SREs with experience operating Firebase-backed applications at scale. Our guides are based on real production incident postmortems, Firebase support case patterns, and contributions to open-source Firebase tooling.

Sources

Related Guides