The UNAVAILABLE error in Firebase Cloud Messaging indicates that the FCM server is temporarily overloaded or under maintenance and cannot process your request at this time.
The "messaging/UNAVAILABLE: Server overloaded" error occurs when Firebase Cloud Messaging (FCM) servers are experiencing high load or undergoing maintenance. This is a server-side error (HTTP 503 Service Unavailable) that indicates the FCM infrastructure cannot currently process your notification request. This error is typically temporary and falls into the 500-599 range of HTTP status codes, which represent server-side failures. Unlike client errors (400-499), this means your request format is correct, but the service cannot fulfill it at this moment due to capacity constraints. The error may also appear alongside HTTP 429 responses during severe overload situations, where the system throttles incoming requests to protect infrastructure stability.
Add retry logic with exponential backoff to handle temporary server overload gracefully. This is the recommended approach from Firebase documentation.
// Node.js example with Firebase Admin SDK
const admin = require('firebase-admin');
async function sendWithRetry(message, maxRetries = 5) {
let retryCount = 0;
let delay = 1000; // Start with 1 second
while (retryCount < maxRetries) {
try {
const response = await admin.messaging().send(message);
console.log('Successfully sent message:', response);
return response;
} catch (error) {
if (error.code === 'messaging/unavailable' ||
error.code === 'messaging/internal-error') {
retryCount++;
if (retryCount >= maxRetries) {
console.error('Max retries reached:', error);
throw error;
}
// Add jitter (randomness) to prevent thundering herd
const jitter = Math.random() * 1000;
const waitTime = delay + jitter;
console.log(`Retry ${retryCount}/${maxRetries} after ${Math.round(waitTime)}ms`);
await new Promise(resolve => setTimeout(resolve, waitTime));
// Exponential backoff: double the delay each time
delay *= 2;
} else {
// Different error, don't retry
throw error;
}
}
}
}
// Usage
const message = {
notification: {
title: 'Hello',
body: 'World'
},
token: 'device-token-here'
};
sendWithRetry(message);The exponential backoff pattern uses delays like: 1s, 2s, 4s, 8s, 16s. Adding jitter prevents all clients from retrying simultaneously.
Before implementing complex solutions, verify if there's a known outage affecting FCM services.
1. Visit the Firebase Status Dashboard: https://status.firebase.google.com/
2. Look for the "Cloud Messaging" section
3. Check for any active incidents or maintenance windows
If there's an ongoing incident, wait for Google to resolve it. Your retry logic will automatically succeed once service is restored.
FCM may include a Retry-After header in the response to indicate when you should retry. Respect this value instead of using your own backoff calculation.
async function sendWithRetryAfterHeader(message) {
try {
const response = await admin.messaging().send(message);
return response;
} catch (error) {
if (error.code === 'messaging/unavailable') {
// Check if there's a Retry-After header in the error details
const retryAfter = error.errorInfo?.retry_after;
if (retryAfter) {
const waitMs = parseInt(retryAfter) * 1000;
console.log(`Server requested retry after ${retryAfter}s`);
await new Promise(resolve => setTimeout(resolve, waitMs));
// Retry once after waiting
return await admin.messaging().send(message);
}
}
throw error;
}
}Always prioritize the server's suggested retry time over your own logic.
Prevent contributing to FCM server overload by implementing proper rate limiting on your side.
// Example using p-queue for rate limiting
const PQueue = require('p-queue').default;
class FCMSender {
constructor() {
// Limit to 100 concurrent requests
this.queue = new PQueue({
concurrency: 100,
interval: 1000, // 1 second
intervalCap: 500 // Max 500 messages per second
});
}
async sendMessage(message) {
return this.queue.add(async () => {
return await admin.messaging().send(message);
});
}
async sendBatch(messages) {
const promises = messages.map(msg => this.sendMessage(msg));
return Promise.allSettled(promises);
}
}
// Usage
const sender = new FCMSender();
await sender.sendBatch(arrayOfMessages);This prevents sudden bursts of requests that could contribute to overload situations.
When sending to multiple devices, use FCM's batch APIs to reduce request overhead and improve efficiency.
// Send to multiple devices efficiently
async function sendBatchNotifications(tokens, notification) {
const messages = tokens.map(token => ({
notification: notification,
token: token
}));
// Send up to 500 messages at once
const batchSize = 500;
const results = [];
for (let i = 0; i < messages.length; i += batchSize) {
const batch = messages.slice(i, i + batchSize);
try {
const response = await admin.messaging().sendEach(batch);
results.push(response);
console.log(`Batch sent: ${response.successCount} succeeded, ${response.failureCount} failed`);
// Small delay between batches
if (i + batchSize < messages.length) {
await new Promise(resolve => setTimeout(resolve, 100));
}
} catch (error) {
console.error('Batch send failed:', error);
// Implement retry logic here
}
}
return results;
}Batching reduces the total number of API calls and is more efficient for FCM servers.
Monitoring and Alerting
Set up monitoring to track FCM error rates and automatically alert when UNAVAILABLE errors exceed a threshold. This helps you distinguish between temporary glitches and sustained outages.
// Example monitoring setup
let unavailableCount = 0;
let totalRequests = 0;
async function sendWithMonitoring(message) {
totalRequests++;
try {
return await admin.messaging().send(message);
} catch (error) {
if (error.code === 'messaging/unavailable') {
unavailableCount++;
const errorRate = unavailableCount / totalRequests;
if (errorRate > 0.1) { // More than 10% failing
// Alert your team
console.error('FCM error rate exceeded 10%:', errorRate);
}
}
throw error;
}
}Queueing System for Resilience
For production systems, consider using a message queue (like Cloud Tasks, Bull, or SQS) to store pending notifications. This allows you to retry failed sends without losing messages during extended outages.
Fallback Strategies
If FCM remains unavailable for extended periods, consider fallback notification channels:
- Email notifications for critical alerts
- SMS for time-sensitive messages
- In-app notification banners when users next open the app
- WebSockets for real-time web applications
HTTP/2 vs Legacy HTTP Protocol
The newer Firebase HTTP v1 API uses HTTP/2, which provides better connection multiplexing and error handling. If you're still using the legacy API, consider migrating to v1 for improved reliability during high-load scenarios.
Regional Considerations
FCM has regional infrastructure. If you experience persistent UNAVAILABLE errors in specific geographic regions, check if there are regional outages or network routing issues affecting connectivity to Google's infrastructure.
messaging/UNSPECIFIED_ERROR: No additional information available
How to fix "messaging/UNSPECIFIED_ERROR: No additional information available" in Firebase Cloud Messaging
App Check: reCAPTCHA Score Too Low
App Check reCAPTCHA Score Too Low
storage/invalid-url: Invalid URL format for Cloud Storage reference
How to fix invalid URL format in Firebase Cloud Storage
auth/missing-uid: User ID identifier required
How to fix "auth/missing-uid: User ID identifier required" in Firebase
auth/invalid-argument: Invalid parameter passed to method
How to fix "auth/invalid-argument: Invalid parameter passed to method" in Firebase