Error Medic

Firebase Rate Limit, 401, 403, 502, 503 & Token Errors: Complete Troubleshooting Guide

Fix Firebase rate limit, 401 unauthorized, 403 forbidden, 502/503 errors, token expired, and connection refused issues with step-by-step diagnostic commands.

Last updated:
Last verified:
2,332 words
Key Takeaways
  • Firebase 429 / RESOURCE_EXHAUSTED errors mean you've exceeded Firestore read/write quotas or Authentication sign-in limits — back off with exponential retry and review your security rules
  • 401 UNAUTHENTICATED and 403 PERMISSION_DENIED errors almost always trace to expired ID tokens, misconfigured Security Rules, or a missing/wrong service-account key — refresh the token or fix the rule before retrying
  • 502 / 503 errors from Firebase are transient infrastructure issues on Google's side; use the Firebase Status Dashboard to confirm, then implement jitter-based retries in your client
  • Token expiry (ID tokens last 1 hour by default) causes silent 401 failures in long-lived server processes — call getIdToken(true) to force-refresh or use the Admin SDK with a service account that never expires
  • Connection refused on firebase-adminsdk calls usually means a wrong project ID, a revoked service-account key, or a missing GOOGLE_APPLICATION_CREDENTIALS env var
Fix Approaches Compared
MethodWhen to UseTime to ApplyRisk
Force-refresh ID token (getIdToken(true))401 in client SDK after >1 hour session< 5 minLow — safe, built-in SDK call
Regenerate & rotate service account key401/403 in Admin SDK or server-side calls10–15 minMedium — must redeploy all services using old key
Rewrite Firestore Security Rules403 PERMISSION_DENIED on specific collection15–30 minMedium — incorrect rules lock out all users
Exponential backoff + jitter retry loop429 rate limit or 503 transient errors30–60 minLow — purely additive, no breaking changes
Increase Firebase quota via GCP ConsolePersistent 429 after backoff; production scale1–2 days (approval)Low — pay-as-you-go billing increase
Switch to Admin SDK with service accountLong-lived backend processes getting 4011–2 hrsLow — more stable than user ID tokens on servers
Enable App CheckPersistent 403 due to unauthenticated abuse2–4 hrsMedium — requires client SDK upgrade
Audit & fix CORS / allowed domains401 on web client, token looks valid15 minLow — config change only

Understanding Firebase Auth & API Errors

Firebase surfaces errors through two layers: the client SDK (which maps HTTP status codes to named error codes like auth/id-token-expired) and the REST/gRPC APIs underneath (which return raw 4xx/5xx HTTP codes). Understanding which layer generated the error is the first diagnostic step.

Error Code Taxonomy

HTTP Code Firebase Error Code Root Cause
400 auth/invalid-email, auth/weak-password Malformed request payload
401 auth/id-token-expired, UNAUTHENTICATED Expired or missing ID token
403 PERMISSION_DENIED, auth/unauthorized-domain Security Rules blocked the request or domain not whitelisted
429 RESOURCE_EXHAUSTED, auth/too-many-requests Rate limit or quota exceeded
502 (no SDK code — raw HTTP) Google infrastructure gateway error
503 (no SDK code — raw HTTP) Firebase service temporarily unavailable

Step 1: Identify Which Service Is Failing

Firebase is a suite of products. The error source matters because the fix differs:

  • Firebase Authentication — sign-in, token issuance, user management
  • Firestore — document reads/writes, real-time listeners
  • Firebase Storage — file uploads/downloads
  • Firebase Functions — HTTP callable and background triggers
  • Firebase Hosting — static asset delivery and rewrites

Diagnostic command — check raw HTTP response:

curl -s -o /dev/null -w "%{http_code}" \
  -H "Authorization: Bearer $(gcloud auth print-identity-token)" \
  "https://firestore.googleapis.com/v1/projects/YOUR_PROJECT_ID/databases/(default)/documents/your-collection"

If you get 401, your token is invalid or expired. 403 means the token is valid but the Security Rules deny access. 429 means quota exhaustion.


Step 2: Diagnose 401 UNAUTHENTICATED

The most common cause is an expired ID token. Firebase ID tokens expire after 3600 seconds (1 hour). Long-running processes, server-side rendering apps, and service workers are frequent victims.

How to confirm token expiry:

# Decode the JWT payload (no library needed)
TOKEN="your.id.token.here"
payload=$(echo $TOKEN | cut -d'.' -f2 | base64 -d 2>/dev/null || echo $TOKEN | cut -d'.' -f2 | base64 --decode 2>/dev/null)
echo $payload | python3 -m json.tool | grep -E '"exp"|"iat"|"aud"'

Compare the exp field (Unix timestamp) against date +%s. If exp < now, the token is expired.

Fix for client SDK (JavaScript):

// Force token refresh before any authenticated request
const user = auth.currentUser;
if (user) {
  const token = await user.getIdToken(/* forceRefresh */ true);
  // Use `token` in your Authorization header
}

Fix for Admin SDK (Node.js server):

Never use user ID tokens on the server. Use a service account:

export GOOGLE_APPLICATION_CREDENTIALS="/path/to/serviceAccountKey.json"

Then initialize with:

import { initializeApp, cert } from 'firebase-admin/app';
const app = initializeApp({ credential: cert('/path/to/serviceAccountKey.json') });

Other 401 causes:

  • Wrong project ID in client config (firebaseConfig.projectId)
  • Revoked service account key — check GCP IAM Console → Service Accounts → Keys
  • Missing Authorization: Bearer header in manual REST calls
  • GOOGLE_APPLICATION_CREDENTIALS env var pointing to a deleted or wrong file

Step 3: Diagnose 403 PERMISSION_DENIED

A 403 means Firebase received your token, validated it, but Security Rules rejected the operation.

Check Security Rules in Firebase Console:

Navigate to Firestore Database → Rules. A common misconfiguration:

// WRONG: Default denies everything after the 30-day trial
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if false; // <-- This blocks everything
    }
  }
}

Use the Rules Playground in Firebase Console to simulate the failing request with your exact UID and path.

403 from Authentication (unauthorized domain):

If your web app is served from a new domain and you see:

FirebaseError: Firebase: This domain is not authorized to run this operation.
(auth/unauthorized-domain)

Go to Firebase Console → Authentication → Settings → Authorized Domains and add your domain.


Step 4: Diagnose Firebase Rate Limit (429 / RESOURCE_EXHAUSTED)

Firebase has two distinct rate-limiting systems:

  1. Firestore quotas (free Spark plan): 50,000 reads/day, 20,000 writes/day, 20,000 deletes/day
  2. Authentication rate limits: 100 sign-ins per IP per minute (default)

Identify quota exhaustion in logs:

GRPC error 8: RESOURCE_EXHAUSTED: Quota exceeded for quota metric 'datastore.googleapis.com/api/request_count'

Check current quota usage:

gcloud firestore operations list --project=YOUR_PROJECT_ID
# Or via GCP Console: APIs & Services → Quotas → search "Firestore"

Implement exponential backoff (JavaScript):

async function firestoreWithRetry(operation, maxRetries = 5) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await operation();
    } catch (err) {
      if (err.code === 'resource-exhausted' || err.status === 429) {
        const delay = Math.min(1000 * Math.pow(2, attempt) + Math.random() * 500, 32000);
        console.warn(`Rate limited. Retrying in ${delay}ms (attempt ${attempt + 1})`);
        await new Promise(r => setTimeout(r, delay));
      } else {
        throw err; // Non-retryable error
      }
    }
  }
  throw new Error('Max retries exceeded');
}

Step 5: Diagnose 502 / 503 Errors

These are transient server-side errors from Google's infrastructure. They are not caused by your code.

Immediate checks:

  1. Visit https://status.firebase.google.com — look for active incidents
  2. Check https://status.cloud.google.com for underlying GCP issues
  3. These errors typically self-resolve within minutes

If 503 persists beyond 10 minutes, check for:

  • Firestore security rules with extremely complex queries causing server-side timeout
  • Cloud Functions that exceed the 540-second timeout (returns 503 to the caller)
  • Hosting rewrites pointing to a deleted Cloud Run service

Cloud Functions timeout fix:

gcloud functions deploy my-function \
  --timeout=540s \
  --memory=512MB \
  --project=YOUR_PROJECT_ID

Step 6: Diagnose Connection Refused

This error (ECONNREFUSED or connect ECONNREFUSED) is almost always a network or environment configuration issue, not a Firebase bug.

Common causes:

  • Firebase emulator is not running but FIREBASE_EMULATOR_HOST env var is set
  • Corporate firewall blocking outbound 443 to *.googleapis.com
  • Docker container with no egress network configured

Test connectivity:

curl -v https://firestore.googleapis.com/
nslookup firestore.googleapis.com
traceroute firestore.googleapis.com

Check for accidental emulator config:

env | grep -i firebase
env | grep -i emulator
# Unset if wrong:
unset FIREBASE_EMULATOR_HOST
unset FIRESTORE_EMULATOR_HOST

Step 7: Monitoring & Prevention

Set up Cloud Monitoring alerts for quota usage:

gcloud alpha monitoring policies create \
  --notification-channels=YOUR_CHANNEL_ID \
  --display-name="Firestore quota warning" \
  --condition-display-name="Reads > 40000/day" \
  --condition-filter='metric.type="firestore.googleapis.com/document/read_count"' \
  --condition-threshold-value=40000 \
  --project=YOUR_PROJECT_ID

Enable Firebase Performance Monitoring in your app to catch latency spikes before they become 503s:

import { getPerformance } from 'firebase/performance';
const perf = getPerformance(app);

Frequently Asked Questions

bash
#!/usr/bin/env bash
# Firebase Diagnostic Script
# Usage: PROJECT_ID=my-project bash firebase-diagnose.sh

set -euo pipefail
PROJECT_ID="${PROJECT_ID:-$(gcloud config get-value project 2>/dev/null)}"

echo "=== Firebase Diagnostic Report ==="
echo "Project: $PROJECT_ID"
echo "Date: $(date -u +"%Y-%m-%dT%H:%M:%SZ")"
echo ""

# 1. Check gcloud auth
echo "--- Auth Status ---"
gcloud auth list --format='value(account,status)' 2>&1 | head -5

# 2. Check GOOGLE_APPLICATION_CREDENTIALS
echo ""
echo "--- Service Account Credentials ---"
if [[ -n "${GOOGLE_APPLICATION_CREDENTIALS:-}" ]]; then
  echo "GOOGLE_APPLICATION_CREDENTIALS=${GOOGLE_APPLICATION_CREDENTIALS}"
  if [[ -f "$GOOGLE_APPLICATION_CREDENTIALS" ]]; then
    echo "File exists: YES"
    python3 -c "import json; d=json.load(open('$GOOGLE_APPLICATION_CREDENTIALS')); print(f'Service Account: {d.get(\"client_email\", \"N/A\")}')"
  else
    echo "File exists: NO — this will cause 401 errors!"
  fi
else
  echo "GOOGLE_APPLICATION_CREDENTIALS is not set"
fi

# 3. Check emulator env vars
echo ""
echo "--- Emulator Env Vars (unexpected values cause connection refused) ---"
env | grep -iE 'emulator|firestore_host|firebase_host' || echo "No emulator vars set"

# 4. Test Firestore API connectivity
echo ""
echo "--- Firestore API Connectivity ---"
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
  --max-time 10 \
  "https://firestore.googleapis.com/v1/projects/${PROJECT_ID}/databases/(default)/documents" \
  -H "Authorization: Bearer $(gcloud auth print-access-token 2>/dev/null)" 2>/dev/null || echo "000")
echo "HTTP Status: $HTTP_CODE"
case $HTTP_CODE in
  200) echo "Result: OK" ;;
  401) echo "Result: 401 — token invalid or expired. Run: gcloud auth login" ;;
  403) echo "Result: 403 — check Security Rules and IAM permissions" ;;
  429) echo "Result: 429 — RATE LIMITED. Wait and retry with backoff" ;;
  50*) echo "Result: $HTTP_CODE — transient Firebase/GCP error. Check https://status.firebase.google.com" ;;
  000) echo "Result: Connection failed — check network / firewall / DNS" ;;
  *)   echo "Result: Unexpected status $HTTP_CODE" ;;
esac

# 5. Test Auth API
echo ""
echo "--- Firebase Auth API Connectivity ---"
AUTH_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
  --max-time 10 \
  "https://identitytoolkit.googleapis.com/v1/projects/${PROJECT_ID}/accounts:lookup" \
  -H "Authorization: Bearer $(gcloud auth print-access-token 2>/dev/null)" \
  -H "Content-Type: application/json" \
  -d '{"idToken": "test"}' 2>/dev/null || echo "000")
echo "HTTP Status: $AUTH_CODE (400 is expected for dummy token — means API is reachable)"

# 6. Decode a JWT token if provided
if [[ -n "${ID_TOKEN:-}" ]]; then
  echo ""
  echo "--- JWT Token Analysis ---"
  PAYLOAD=$(echo "$ID_TOKEN" | cut -d'.' -f2 | tr '_-' '/+' | \
    awk '{while(length%4!=0) $0=$0"="; print}' | base64 -d 2>/dev/null)
  echo "$PAYLOAD" | python3 -m json.tool 2>/dev/null | grep -E '"exp"|"iat"|"aud"|"sub"'
  NOW=$(date +%s)
  EXP=$(echo "$PAYLOAD" | python3 -c "import sys,json; print(json.load(sys.stdin).get('exp',0))" 2>/dev/null || echo 0)
  if [[ $EXP -lt $NOW ]]; then
    echo "Token EXPIRED at $(date -d "@${EXP}" 2>/dev/null || date -r $EXP 2>/dev/null || echo $EXP)"
  else
    TTLSECS=$((EXP - NOW))
    echo "Token valid for ${TTLSECS}s more (expires $(date -d "@${EXP}" 2>/dev/null || echo $EXP))"
  fi
else
  echo ""
  echo "Tip: set ID_TOKEN=<your_firebase_id_token> to analyze token expiry"
fi

echo ""
echo "=== Diagnostic complete ==="
E

Error Medic Editorial

Error Medic Editorial is a team of senior DevOps and SRE engineers with experience operating Firebase, GCP, and AWS at scale. We write actionable troubleshooting guides grounded in production incident postmortems.

Sources

Related Articles in Firebase

Explore More API Errors Guides