Error Medic

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

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

Last updated:
Last verified:
2,038 words
Key Takeaways
  • Firebase 401/unauthorized errors almost always mean an expired or malformed ID token — refresh it before every request using getIdToken(true)
  • Rate limiting (HTTP 429) triggers when you exceed Firestore read/write quotas or Authentication sign-in limits; use exponential backoff and batch writes to stay under quota
  • 502 and 503 responses are Firebase infrastructure transients — implement retry logic with jitter; persistent 503s on Cloud Functions indicate cold-start timeouts or memory limits
  • Firebase 403 (permission denied) is a security rules mismatch, not an auth failure — test rules in the Firebase Rules Playground before deploying
  • Invalid or expired tokens are the root cause of 60%+ of Firebase auth failures; always validate token expiry client-side before making API calls
Firebase Error Fix Approaches Compared
MethodWhen to UseTime to ImplementRisk
Force token refresh (getIdToken(true))401 on valid user session, token > 55 min old5 minutesLow — safe idempotent call
Exponential backoff with jitter429 rate limit, 503 service unavailable30 minutesLow — standard retry pattern
Rewrite Firestore Security Rules403 permission denied on valid auth15–60 minutesMedium — can lock out users if wrong
Batch writes / transactionsHitting Firestore write quota (20k/min)1–4 hoursLow — improves throughput and atomicity
Increase Cloud Function timeout/memory503 on Functions, connection refused10 minutesLow — increases cost slightly
Upgrade Firebase plan (Spark → Blaze)Persistent quota exhaustion on free tier5 minutesLow — pay-as-you-go billing starts
Firebase App Check enforcement401 from unauthenticated app abuse2–8 hoursMedium — requires client SDK update
Regional endpoint selectionPersistent 502 latency spikes20 minutesLow — transparent to end users

Understanding Firebase Rate Limit and Auth Errors

Firebase surfaces errors across two distinct layers: the Authentication service (identity tokens, sign-in methods) and the backend services (Firestore, Realtime Database, Cloud Functions, Storage). Each layer has its own quota system, and a failure in one often cascades into errors that look like auth problems in another.

The most common error messages developers encounter:

FirebaseError: Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. Got 'null'.
FirebaseError: PERMISSION_DENIED: Missing or insufficient permissions.
POST https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword 400
FirebaseError: TOO_MANY_REQUESTS
[auth/network-request-failed] A network error occurred
HTTP 401 Unauthorized
HTTP 403 Forbidden
HTTP 429 Too Many Requests
HTTP 502 Bad Gateway
HTTP 503 Service Unavailable

Step 1: Identify the Error Layer

Before fixing anything, determine whether the error originates from Firebase Authentication or a downstream service (Firestore, Functions, Storage).

Check the Firebase Authentication service status:

https://status.firebase.google.com

In your browser console or server logs, look at the full request URL in the failed response:

  • identitytoolkit.googleapis.com → Authentication layer
  • firestore.googleapis.com → Firestore
  • cloudfunctions.net or run.app → Cloud Functions
  • storage.googleapis.com → Firebase Storage

Extract the error code programmatically:

try {
  await signInWithEmailAndPassword(auth, email, password);
} catch (error) {
  console.error('Code:', error.code);       // e.g. 'auth/too-many-requests'
  console.error('Message:', error.message); // Human-readable
}

Common error.code values and their root causes:

Code Meaning
auth/id-token-expired Token older than 1 hour, needs refresh
auth/invalid-id-token Token malformed or from wrong project
auth/too-many-requests IP or account temporarily blocked
auth/network-request-failed DNS/TLS failure or Firebase outage
auth/unauthorized-domain App domain not whitelisted in Firebase console
permission-denied Security rules rejected the request

Step 2: Fix Token Expiration (401 / auth/id-token-expired)

Firebase ID tokens expire after 60 minutes. The Firebase SDK auto-refreshes them — but only if you use onAuthStateChanged correctly. Calling getIdToken() without the forceRefresh flag returns the cached (possibly expired) token.

Wrong approach (uses stale token):

const token = await user.getIdToken(); // May return expired token from cache

Correct approach:

// Force refresh when token is near expiry
const token = await user.getIdToken(true); // Forces network call to refresh

// Better: use an Axios interceptor to auto-refresh on 401
axios.interceptors.response.use(
  response => response,
  async error => {
    if (error.response?.status === 401) {
      const user = auth.currentUser;
      if (user) {
        const freshToken = await user.getIdToken(true);
        error.config.headers['Authorization'] = `Bearer ${freshToken}`;
        return axios(error.config); // Retry with new token
      }
    }
    return Promise.reject(error);
  }
);

Server-side token verification (Node.js Admin SDK):

const admin = require('firebase-admin');

async function verifyToken(idToken) {
  try {
    const decoded = await admin.auth().verifyIdToken(idToken);
    return decoded;
  } catch (error) {
    if (error.code === 'auth/id-token-expired') {
      // Token expired — client must refresh
      throw new Error('TOKEN_EXPIRED');
    }
    if (error.code === 'auth/argument-error') {
      // Malformed token — do not retry
      throw new Error('INVALID_TOKEN');
    }
    throw error;
  }
}

Step 3: Fix Rate Limiting (429 / TOO_MANY_REQUESTS)

Firebase enforces rate limits at multiple levels:

  • Authentication: 100 sign-up/sign-in requests per IP per minute (adjustable in console)
  • Firestore: 1 write per second per document; 10,000 writes per second per database on Blaze plan
  • Cloud Functions: 3,000 concurrent executions on Blaze; automatic scale-up with cold start latency

Implement exponential backoff with jitter:

async function withRetry(fn, maxRetries = 5) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      const isRateLimit = 
        error.code === 'resource-exhausted' || 
        error.status === 429 ||
        error.message?.includes('TOO_MANY_REQUESTS');
      
      if (!isRateLimit || attempt === maxRetries) throw error;
      
      // Exponential backoff: 1s, 2s, 4s, 8s, 16s + jitter
      const base = Math.pow(2, attempt) * 1000;
      const jitter = Math.random() * 1000;
      await new Promise(resolve => setTimeout(resolve, base + jitter));
    }
  }
}

// Usage
await withRetry(() => setDoc(doc(db, 'users', uid), data));

Use Firestore batch writes to reduce operation count:

const batch = writeBatch(db);

users.forEach(user => {
  const ref = doc(db, 'users', user.id);
  batch.set(ref, user); // Counts as 1 write regardless of batch size
});

await batch.commit(); // Single network round-trip, up to 500 docs

Step 4: Fix Permission Denied (403 / PERMISSION_DENIED)

A 403 means your Security Rules rejected the request. This is not the same as being unauthenticated — a logged-in user can still receive 403 if rules don't allow the operation.

Test rules without deploying using the Rules Playground:

  1. Firebase Console → Firestore → Rules → Rules Playground
  2. Set Auth UID to your test user's UID
  3. Simulate the exact read/write path

Common Firestore rules mistakes:

// BAD: Allows read but denies write — often confused with auth error
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{userId} {
      allow read: if request.auth != null;
      // Missing write rule defaults to DENY
    }
  }
}

// GOOD: Explicit rules with ownership check
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{userId} {
      allow read: if request.auth != null;
      allow write: if request.auth != null && request.auth.uid == userId;
    }
  }
}

Validate rules with the Firebase CLI:

npm install -g firebase-tools
firebase login

# Test rules locally before deploying
firebase emulators:start --only firestore

# Deploy rules
firebase deploy --only firestore:rules

Step 5: Fix 502 / 503 / Connection Refused

HTTP 502 (Bad Gateway) and 503 (Service Unavailable) from Firebase are almost always transient infrastructure issues or Cloud Functions problems.

For Cloud Functions 503 / connection refused:

# Check function logs for timeout or memory errors
firebase functions:log --only myFunction

# Increase timeout and memory in function definition
exports.myFunction = functions
  .runWith({ timeoutSeconds: 300, memory: '512MB' })
  .https.onRequest(async (req, res) => {
    // ...
  });

Reduce cold start latency with minimum instances:

exports.criticalApi = functions
  .runWith({ minInstances: 1 }) // Keeps 1 instance warm
  .https.onRequest(handler);

Test connectivity to Firebase endpoints:

curl -I https://firestore.googleapis.com
curl -I https://identitytoolkit.googleapis.com
nslookup firestore.googleapis.com

Frequently Asked Questions

bash
#!/usr/bin/env bash
# Firebase Diagnostic Script
# Run this to collect evidence before filing a support ticket

set -euo pipefail

PROJECT_ID="${FIREBASE_PROJECT_ID:-your-project-id}"

echo "=== Firebase Connectivity Check ==="
curl -s -o /dev/null -w "identitytoolkit: %{http_code}\n" \
  https://identitytoolkit.googleapis.com
curl -s -o /dev/null -w "firestore: %{http_code}\n" \
  https://firestore.googleapis.com
curl -s -o /dev/null -w "firebase status page: %{http_code}\n" \
  https://status.firebase.google.com

echo ""
echo "=== DNS Resolution ==="
nslookup firestore.googleapis.com | grep -E 'Address|Name'
nslookup identitytoolkit.googleapis.com | grep -E 'Address|Name'

echo ""
echo "=== Firebase CLI Auth Status ==="
firebase login:list 2>/dev/null || echo "firebase-tools not installed or not logged in"

echo ""
echo "=== Project Quota (requires gcloud) ==="
if command -v gcloud &>/dev/null; then
  gcloud auth application-default print-access-token &>/dev/null && \
    gcloud services quota list \
      --service=firestore.googleapis.com \
      --project="$PROJECT_ID" \
      --format="table(metric, currentUsage, limit)" 2>/dev/null || \
    echo "Run: gcloud auth application-default login"
fi

echo ""
echo "=== Recent Cloud Functions Errors ==="
if command -v firebase &>/dev/null; then
  firebase functions:log \
    --project="$PROJECT_ID" \
    --lines=50 2>/dev/null | \
    grep -iE 'error|exception|rate.limit|quota|timeout|503|502|401|403' || \
    echo "No matching error lines found"
fi

echo ""
echo "=== Test Token Verification (Node.js) ==="
cat <<'EOF'
// Save as check-token.js and run: node check-token.js <idToken>
const admin = require('firebase-admin');
admin.initializeApp(); // Uses GOOGLE_APPLICATION_CREDENTIALS env var

const token = process.argv[2];
if (!token) { console.error('Usage: node check-token.js <idToken>'); process.exit(1); }

admin.auth().verifyIdToken(token)
  .then(decoded => {
    const expiresIn = decoded.exp - Math.floor(Date.now() / 1000);
    console.log('Token valid. UID:', decoded.uid);
    console.log('Expires in:', expiresIn, 'seconds');
    console.log('Issued at:', new Date(decoded.iat * 1000).toISOString());
    console.log('Claims:', JSON.stringify(decoded, null, 2));
  })
  .catch(err => {
    console.error('Token verification FAILED');
    console.error('Code:', err.code);
    console.error('Message:', err.message);
  });
EOF

echo ""
echo "=== Firestore Rules Emulator Test ==="
cat <<'EOF'
# Install and start the emulator
npm install -g firebase-tools
firebase emulators:start --only firestore --project demo-test

# In another terminal, test rules with the REST API
curl -X POST \
  'http://localhost:8080/v1/projects/demo-test/databases/(default)/documents/users' \
  -H 'Authorization: Bearer owner' \
  -H 'Content-Type: application/json' \
  -d '{"fields": {"name": {"stringValue": "test"}}}'
EOF

echo ""
echo "Diagnostic complete. Check output above for anomalies."
E

Error Medic Editorial

Error Medic Editorial is a team of senior DevOps and SRE engineers with combined experience across Google Cloud, Firebase, AWS, and Azure. We specialize in production incident postmortems, quota management, and distributed systems reliability. Our guides are tested against real production workloads before publication.

Sources

Related Articles in Firebase

Explore More API Errors Guides