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 Guides