The FCM registration token is no longer valid because the app was uninstalled, the token expired after 270 days of inactivity, or the app instance was unregistered from Firebase Cloud Messaging.
This error occurs when Firebase Cloud Messaging (FCM) attempts to send a push notification to a registration token that is no longer valid. FCM maintains a registry of device tokens, and when a token becomes invalid, the service returns an UNREGISTERED error (HTTP 404). Registration tokens can become unregistered for several reasons: the user uninstalled the app, the app explicitly unregistered itself from FCM, Google refreshed the token pool, or the token reached the 270-day inactivity threshold. On iOS, this can also happen if the APNs Feedback Service reports the APNs token as invalid. When you receive this error, it means FCM has already removed the token from its registry and any future attempts to send messages to this token will fail. The token cannot be revived and must be deleted from your server database.
Update your server code to catch and process UNREGISTERED errors when sending notifications:
// Node.js Admin SDK
const admin = require('firebase-admin');
async function sendNotification(token, payload) {
try {
const response = await admin.messaging().send({
token: token,
notification: payload,
});
console.log('Successfully sent message:', response);
return { success: true };
} catch (error) {
if (error.code === 'messaging/registration-token-not-registered' ||
error.code === 'messaging/invalid-argument') {
console.log('Token is unregistered, removing from database');
await removeTokenFromDatabase(token);
return { success: false, removed: true };
}
throw error;
}
}The key is to check for messaging/registration-token-not-registered error code and delete the token when detected.
Create a function to delete unregistered tokens from your server:
async function removeTokenFromDatabase(token) {
// Example using a generic database
await db.collection('fcm_tokens').deleteOne({ token: token });
console.log(`Removed invalid token: ${token}`);
}
// Or with PostgreSQL
async function removeTokenFromDatabase(token) {
await pool.query('DELETE FROM fcm_tokens WHERE token = $1', [token]);
}Never attempt to resend to tokens that returned UNREGISTERED errors.
Track when tokens were last seen active to proactively remove stale tokens:
// When saving a new token
async function saveToken(userId, token) {
await db.collection('fcm_tokens').updateOne(
{ userId: userId },
{
$set: {
token: token,
lastSeen: new Date(),
createdAt: new Date(),
}
},
{ upsert: true }
);
}
// Update lastSeen when app opens
async function updateTokenActivity(token) {
await db.collection('fcm_tokens').updateOne(
{ token: token },
{ $set: { lastSeen: new Date() } }
);
}This allows you to identify tokens approaching the 270-day inactivity limit.
Create a scheduled task to remove tokens that are likely expired:
// Cloud Function running daily
exports.cleanupStaleTokens = functions.pubsub
.schedule('every 24 hours')
.onRun(async (context) => {
const staleDate = new Date();
staleDate.setDate(staleDate.getDate() - 250); // 250 days ago
const staleTokens = await db.collection('fcm_tokens')
.where('lastSeen', '<', staleDate)
.get();
const batch = db.batch();
staleTokens.forEach((doc) => {
batch.delete(doc.ref);
});
await batch.commit();
console.log(`Removed ${staleTokens.size} stale tokens`);
});Remove tokens before they hit 270 days to prevent unnecessary send attempts.
Update your client app to retrieve and send fresh tokens on each startup:
// React Native / JavaScript
import messaging from '@react-native-firebase/messaging';
async function checkAndRefreshToken() {
const authStatus = await messaging().requestPermission();
if (authStatus === messaging.AuthorizationStatus.AUTHORIZED) {
const token = await messaging().getToken();
// Send to your server
await fetch('https://yourserver.com/api/tokens', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
userId: currentUserId,
token: token,
platform: Platform.OS
}),
});
}
}
// Call on app startup
useEffect(() => {
checkAndRefreshToken();
}, []);This ensures your server always has the most current token.
Listen for token refresh events and update your server:
// React Native
messaging().onTokenRefresh(async (newToken) => {
console.log('Token refreshed:', newToken);
await fetch('https://yourserver.com/api/tokens/refresh', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
userId: currentUserId,
token: newToken
}),
});
});// Android
FirebaseMessaging.getInstance().getToken()
.addOnCompleteListener(task -> {
if (task.isSuccessful()) {
String token = task.getResult();
sendTokenToServer(token);
}
});Token refresh events are rare but critical to handle.
Token Lifecycle Management:
FCM tokens are not permanent identifiers. They can be refreshed by Google at any time, though this is rare in practice. The 270-day inactivity threshold is the most common cause of expiration.
Multiple Tokens Per User:
Users can have multiple tokens if they use your app on different devices. Your database schema should support multiple tokens per user ID and track which device each token belongs to.
APNs Integration (iOS):
On iOS, FCM tokens are tied to APNs tokens. If the APNs token becomes invalid (reported by Apple's APNs Feedback Service), the FCM token will also become unregistered. Monitor APNs responses separately.
Sender ID Constraints:
Registration tokens are bound to the sender ID that created them. If you change your FCM sender ID or Firebase project, all existing tokens become invalid and users must re-register.
Batch Operations:
When sending to multiple tokens, use FCM's batch send methods (sendMulticast or sendAll). These methods return granular success/failure information for each token, making it easier to identify and remove unregistered tokens in bulk.
Testing Token Validity:
Never send actual notifications just to test if a token is valid. Instead, validate tokens when they're first received by checking their format (152+ characters, alphanumeric with colons and hyphens). FCM does not provide a dedicated token validation endpoint.
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