This error occurs when Firebase Cloud Messaging rejects a registration token because it is malformed, corrupted, expired, or doesn't match the expected FCM token format. The fix involves validating token generation, ensuring tokens aren't modified during transmission, and removing invalid tokens from your database.
The "messaging/invalid-registration-token" error with "Token format incorrect" indicates that the registration token provided to Firebase Cloud Messaging (FCM) does not match the expected format for a valid FCM token. FCM registration tokens are unique identifiers that allow your server to send push notifications to specific devices. This error can occur for several reasons: the token string may have been truncated or modified during storage or transmission, extra characters may have been appended, the token may be from a different push notification service (like APNS or OneSignal), or the token generation process may have failed silently on the client side. Unlike the "registration-token-not-registered" error which indicates a valid but expired token, this error specifically means the token string itself is malformed or doesn't conform to FCM's expected format, typically indicating a data handling issue rather than a token lifecycle issue.
First, check that you're using a genuine FCM registration token. Valid FCM tokens are typically long strings (150+ characters) with alphanumeric characters, hyphens, and underscores.
Print the exact token before sending to inspect it:
// Node.js Admin SDK
console.log('Token length:', registrationToken.length);
console.log('Token preview:', registrationToken.substring(0, 50) + '...');
console.log('Token type:', typeof registrationToken);
// Check for common issues
if (registrationToken.includes(' ')) {
console.error('Token contains whitespace!');
}
if (registrationToken.startsWith('"') || registrationToken.endsWith('"')) {
console.error('Token has quotes!');
}Valid FCM tokens should not contain whitespace, quotes, or other unusual characters. They should be a single continuous string.
Ensure your database field can store the full token length and that no truncation occurs during save/retrieval operations.
-- Check your database schema
-- FCM tokens can be 200+ characters
ALTER TABLE users ALTER COLUMN fcm_token TYPE VARCHAR(255);
-- Verify stored token integrity
SELECT
user_id,
LENGTH(fcm_token) as token_length,
fcm_token
FROM users
WHERE fcm_token IS NOT NULL;Also verify your API doesn't trim or modify tokens:
// Bad - may trim whitespace or modify string
const token = req.body.token.trim();
// Good - use exact token from client
const token = req.body.token;
// Store in database
await db.users.update({
where: { id: userId },
data: { fcmToken: token } // Store exactly as received
});Ensure the client app correctly generates and sends FCM tokens. Check for proper error handling during token retrieval.
Web (JavaScript):
import { getMessaging, getToken } from 'firebase/messaging';
async function getFCMToken() {
try {
const messaging = getMessaging();
const token = await getToken(messaging, {
vapidKey: 'YOUR_VAPID_KEY'
});
if (!token) {
throw new Error('No registration token available');
}
console.log('FCM Token:', token);
return token;
} catch (error) {
console.error('Error getting FCM token:', error);
// Don't send null/undefined to server
throw error;
}
}Android (Kotlin):
FirebaseMessaging.getInstance().token.addOnCompleteListener { task ->
if (!task.isSuccessful) {
Log.w(TAG, "Fetching FCM token failed", task.exception)
return@addOnCompleteListener
}
val token = task.result
Log.d(TAG, "FCM Token: $token")
// Send to your server
sendTokenToServer(token)
}iOS (Swift):
Messaging.messaging().token { token, error in
if let error = error {
print("Error fetching FCM token: \(error)")
return
}
if let token = token {
print("FCM Token: \(token)")
self.sendTokenToServer(token)
}
}Add validation before attempting to send notifications to catch malformed tokens early.
// Node.js Admin SDK
function validateFCMToken(token) {
// Basic format checks
if (!token || typeof token !== 'string') {
return { valid: false, reason: 'Token is null or not a string' };
}
if (token.length < 100) {
return { valid: false, reason: 'Token too short (likely truncated)' };
}
if (token.includes(' ') || token.includes('\n')) {
return { valid: false, reason: 'Token contains whitespace' };
}
if (!/^[a-zA-Z0-9_-]+$/.test(token)) {
return { valid: false, reason: 'Token contains invalid characters' };
}
return { valid: true };
}
// Use before sending
const validation = validateFCMToken(registrationToken);
if (!validation.valid) {
console.error('Invalid token:', validation.reason);
// Remove from database or mark as invalid
await db.users.update({
where: { fcmToken: registrationToken },
data: { fcmToken: null, fcmTokenInvalidatedAt: new Date() }
});
throw new Error(`Invalid FCM token: ${validation.reason}`);
}
// Proceed with sending
await admin.messaging().send({
token: registrationToken,
notification: {
title: 'Test',
body: 'Message'
}
});When you encounter this error, remove the invalid token from your database to prevent repeated failures.
// Node.js Admin SDK with error handling
async function sendNotification(userId, message) {
const user = await db.users.findUnique({ where: { id: userId } });
if (!user.fcmToken) {
throw new Error('User has no FCM token');
}
try {
const response = await admin.messaging().send({
token: user.fcmToken,
notification: message
});
console.log('Successfully sent:', response);
return response;
} catch (error) {
console.error('FCM send error:', error.code, error.message);
// Handle invalid token errors
if (error.code === 'messaging/invalid-registration-token' ||
error.code === 'messaging/registration-token-not-registered') {
console.log('Removing invalid token for user:', userId);
await db.users.update({
where: { id: userId },
data: {
fcmToken: null,
fcmTokenInvalidatedAt: new Date(),
fcmTokenInvalidReason: error.code
}
});
}
throw error;
}
}For batch sends, handle errors individually:
// Send to multiple tokens with error handling
const response = await admin.messaging().sendEachForMulticast({
tokens: registrationTokens,
notification: {
title: 'Batch Message',
body: 'Content'
}
});
// Process responses
const tokensToRemove = [];
response.responses.forEach((resp, idx) => {
if (!resp.success) {
const error = resp.error;
console.error(`Token ${idx} failed:`, error.code);
if (error.code === 'messaging/invalid-registration-token' ||
error.code === 'messaging/registration-token-not-registered') {
tokensToRemove.push(registrationTokens[idx]);
}
}
});
// Bulk remove invalid tokens
if (tokensToRemove.length > 0) {
await db.users.updateMany({
where: { fcmToken: { in: tokensToRemove } },
data: { fcmToken: null }
});
console.log(`Removed ${tokensToRemove.length} invalid tokens`);
}Token format vs. token validity: The "invalid-registration-token" error can indicate two different issues depending on the exact message. "Token format incorrect" specifically means the string is malformed, while "not a valid FCM registration token" can also mean the token was once valid but is now expired. Check the exact error message to determine which case you're dealing with.
Cross-platform token mixing: If you support multiple platforms, ensure you're not accidentally using APNS device tokens for Android devices or vice versa. APNS tokens and FCM tokens have different formats and are not interchangeable. Store the platform type alongside each token.
Token refresh on client: Implement token refresh listeners on the client side to ensure your server always has the latest valid token. Tokens can be rotated by FCM, and failing to update them will eventually lead to invalid token errors:
// Web
onMessage(messaging, (payload) => {
console.log('Message received:', payload);
});
// Android
override fun onNewToken(token: String) {
Log.d(TAG, "Refreshed token: $token")
sendTokenToServer(token)
}Case sensitivity: FCM tokens are case-sensitive. Ensure your database collation preserves case, and never perform case-insensitive comparisons or transformations on tokens.
Testing with invalid tokens: You can test your error handling by intentionally sending to a malformed token like "invalid-token-12345". FCM will immediately reject it, allowing you to verify your error handling logic works correctly.
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