How to fix messaging/invalid-argument in Firebase
The Firebase Cloud Messaging "messaging/invalid-argument" error occurs when FCM rejects a send request due to invalid parameters, malformed tokens, oversized payloads, or incorrect message structure. This is a critical error that prevents messages from being delivered to devices.
Quick fix
Fixes messaging/invalid-argument
const registrationToken = '...'; // Should be ~152 characters
// Check token format
if (!registrationToken || registrationToken.length < 100) {
console.error('Token appears invalid - too short or missing');
}
// Token format validation
const fcmTokenRegex = /^[a-zA-Z0-9_-]{100,200}$/;
if (!fcmTokenRegex.test(registrationToken)) {
console.warn('Token format may be invalid');
}
// Verify token before sending
console.log('Token length:', registrationToken.length); // Should be ~152
console.log('Token start:', registrationToken.substring(0, 20));- Verify the registration token format and validity
- Check for batch size limits
- Validate message payload size
- Fix message priority for Apple devices
const registrationToken = '...'; // Should be ~152 characters// Check token formatif (!registrationToken || registrationToken.length < 100) {console.error('Token appears invalid - too short or missing');}// Token format validationconst fcmTokenRegex = /^[a-zA-Z0-9_-]{100,200}$/;if (!fcmTokenRegex.test(registrationToken)) {console.warn('Token format may be invalid');}// Verify token before sendingconsole.log('Token length:', registrationToken.length); // Should be ~152console.log('Token start:', registrationToken.substring(0, 20));messaging/invalid-argument
What this error means
The "messaging/invalid-argument" error in Firebase Cloud Messaging (FCM) is returned by the FCM service when a message request violates one of FCM's validation rules. Unlike client-side parameter errors, this error comes from the FCM API server after validating the complete request. This error can indicate several different problems depending on context: invalid device registration tokens, message payloads that exceed size limits, sending to more than 1000 tokens in a single request, incorrect message structure for the target platform, or invalid priority settings for specific device types. The error code "messaging/invalid-argument" is a broad error category that requires examining the error message details and your request parameters to determine the root cause. Each cause requires a different fix approach.
Typical symptoms
- FCM send requests fail with "messaging/invalid-argument" error response
- Error occurs intermittently only for certain device tokens or message types
- Messages fail to send after device updates or app reinstalls on some devices
- Bulk send operations fail but single-device sends work
- Push notifications using specific features (like content-available) fail
- iOS/Android silent notifications rejected by FCM
- Error occurs when sending data messages with high priority to Apple devices
Common causes
- Registration token is invalid, expired, or malformed (most common)
- Batch send exceeds 1000 device tokens per request (FCM maximum)
- Message payload exceeds maximum size (4096 bytes total, 4000 bytes for notifications)
- Message priority set to "high" for data-only messages or Apple devices (must be "normal")
- Invalid APNs token format when sending to iOS devices
- Registration token is stale (inactive for 270+ days on Android)
- Nested message structure is incorrect or incomplete
- Token format validation fails (expected 152 character format)
- Sending silent notifications (content-available) with invalid token
- Version mismatch between client token and server expectations
How to fix "messaging/invalid-argument"
The most common cause of this error is an invalid or incorrectly formatted registration token. Check your token format:
const registrationToken = '...'; // Should be ~152 characters
// Check token format
if (!registrationToken || registrationToken.length < 100) {
console.error('Token appears invalid - too short or missing');
}
// Token format validation
const fcmTokenRegex = /^[a-zA-Z0-9_-]{100,200}$/;
if (!fcmTokenRegex.test(registrationToken)) {
console.warn('Token format may be invalid');
}
// Verify token before sending
console.log('Token length:', registrationToken.length); // Should be ~152
console.log('Token start:', registrationToken.substring(0, 20));If the token is invalid, stale, or malformed:
1. Request a fresh token from the client device
2. Delete the invalid token from your database
3. Ask the user to reinstall the app if the token won't refresh
4. Implement token refresh logic with expiration handling
FCM has a hard limit of 1000 registration tokens per send request. Split larger batches:
const registrationTokens = [/* array of tokens */];
// Check batch size
if (registrationTokens.length > 1000) {
console.warn(`Batch size is ${registrationTokens.length} - exceeds FCM limit of 1000`);
}
// Correct way to send to 1000+ devices
async function sendToLargeDeviceSet(tokens, message) {
const BATCH_SIZE = 500; // Use smaller batches for reliability
for (let i = 0; i < tokens.length; i += BATCH_SIZE) {
const batch = tokens.slice(i, i + BATCH_SIZE);
try {
const response = await admin.messaging().sendMulticast({
tokens: batch,
notification: message.notification,
data: message.data,
android: message.android,
apns: message.apns,
webpush: message.webpush
});
console.log(`Batch ${Math.floor(i / BATCH_SIZE) + 1}: Success: ${response.successCount}, Failed: ${response.failureCount}`);
// Handle failures
response.responses.forEach((resp, index) => {
if (!resp.success && resp.error) {
const token = batch[index];
console.error(`Failed to send to ${token}: ${resp.error.code}`);
// Delete invalid tokens
if (resp.error.code === 'messaging/invalid-registration-token' ||
resp.error.code === 'messaging/invalid-argument') {
// Delete from your database
await deleteTokenFromDatabase(token);
}
}
});
} catch (error) {
console.error(`Batch ${Math.floor(i / BATCH_SIZE) + 1} failed:`, error);
}
}
}
// Send to device set
await sendToLargeDeviceSet(registrationTokens, message);Always batch requests to 500-1000 tokens to avoid hitting limits.
Firebase has strict payload size limits. Check your message size before sending:
const message = {
token: registrationToken,
notification: {
title: 'Notification Title',
body: 'Notification body text'
},
data: {
key1: 'value1',
key2: 'value2'
}
};
// Check payload size
function checkMessageSize(message) {
const serialized = JSON.stringify(message);
const bytes = new TextEncoder().encode(serialized).length;
console.log(`Total payload size: ${bytes} bytes`);
console.log('FCM limits:');
console.log(' - Notification: 4000 bytes');
console.log(' - Data object: 4000 bytes');
console.log(' - Total message: 4096 bytes');
if (bytes > 4096) {
console.error('ERROR: Message exceeds 4096 byte limit');
return false;
}
if (message.notification) {
const notifSize = JSON.stringify(message.notification).length;
if (notifSize > 4000) {
console.error('ERROR: Notification payload exceeds 4000 bytes');
return false;
}
}
if (message.data) {
const dataSize = JSON.stringify(message.data).length;
if (dataSize > 4000) {
console.error('ERROR: Data payload exceeds 4000 bytes');
return false;
}
}
return true;
}
// Validate before sending
if (!checkMessageSize(message)) {
console.error('Message is too large - reduce content or split into multiple messages');
// Truncate or remove non-essential data
message.data = message.data ? { /* essential fields only */ } : undefined;
}
await admin.messaging().send(message);Keep payloads small:
- Notification title/body: maximum 240 characters combined
- Data values: keep each value under 1000 characters
- Total message: under 4096 bytes
iOS and APNs have strict priority requirements. High priority data messages are rejected:
const message = {
token: apnsToken,
notification: {
title: 'Title',
body: 'Body'
},
data: {
customKey: 'customValue'
}
};
// WRONG: High priority for data-only message on iOS
// await messaging.send({
// token: apnsToken,
// data: { key: 'value' },
// webpush: { headers: { TTL: '3600' } },
// // Missing apns, causes INVALID_ARGUMENT
// });
// CORRECT: Proper priority handling for different platforms
const correctMessage = {
token: apnsToken,
notification: {
title: 'Push Title',
body: 'Push Body'
},
data: {
action: 'open_details',
id: '123'
},
// For iOS/APNs: Priority MUST be normal (5) or omitted for silent
apns: {
headers: {
'apns-priority': '10' // 10 for alerts, 5 for background
},
payload: {
aps: {
alert: {
title: 'Push Title',
body: 'Push Body'
},
sound: 'default',
'content-available': 0 // Set to 1 only for silent notifications
}
}
},
// For Android: Can use high priority
android: {
priority: 'high', // Only use high for time-sensitive messages
notification: {
title: 'Push Title',
body: 'Push Body'
}
}
};
await admin.messaging().send(correctMessage);
// Platform-specific sending
async function sendPlatformSpecific(token, isIOS) {
if (isIOS) {
// iOS: Use normal priority, support content-available
return messaging.send({
token,
notification: { title: 't', body: 'b' },
apns: {
headers: { 'apns-priority': '10' },
payload: { aps: { sound: 'default' } }
}
});
} else {
// Android: Can use high priority
return messaging.send({
token,
notification: { title: 't', body: 'b' },
android: { priority: 'high' }
});
}
}Key rules:
- iOS high-priority messages must include a notification
- Data-only messages to iOS must use normal priority (apns-priority: 5)
- Silent notifications (content-available) should not use high priority
- Android can use high priority for any message type
Stale tokens from inactive devices cause INVALID_ARGUMENT errors. Implement token lifecycle management:
// Track token age
interface TokenRecord {
token: string;
userId: string;
createdAt: Date;
lastUsedAt: Date;
platform: 'ios' | 'android' | 'web';
}
// Mark tokens as stale if inactive for 270+ days
async function removeStaleTokens() {
const STALE_THRESHOLD = 270 * 24 * 60 * 60 * 1000; // 270 days in ms
const now = new Date();
const staleTokens = await db.tokens.find({
lastUsedAt: {
$lt: new Date(now - STALE_THRESHOLD)
}
});
console.log(`Found ${staleTokens.length} stale tokens`);
// Delete stale tokens
for (const token of staleTokens) {
await db.tokens.deleteOne({ token: token.token });
}
}
// Handle token errors during send
async function sendWithTokenCleanup(token, message) {
try {
return await admin.messaging().send({
token,
...message
});
} catch (error) {
// Delete invalid or unregistered tokens
if (error.code === 'messaging/invalid-registration-token' ||
error.code === 'messaging/registration-token-not-registered' ||
error.code === 'messaging/invalid-argument') {
console.log(`Deleting invalid token: ${token}`);
await db.tokens.deleteOne({ token });
// Notify user or re-request token
await db.users.updateOne(
{ tokens: token },
{ $pull: { tokens: token } }
);
}
throw error;
}
}
// Periodic cleanup
setInterval(removeStaleTokens, 24 * 60 * 60 * 1000); // Daily
// On successful send, update lastUsedAt
async function sendAndTrack(token, message) {
const result = await sendWithTokenCleanup(token, message);
await db.tokens.updateOne(
{ token },
{ lastUsedAt: new Date() }
);
return result;
}Implement:
- Track token creation and last used dates
- Delete tokens older than 270 days
- Delete tokens immediately after INVALID_ARGUMENT errors
- Request fresh tokens periodically from clients
Get full error details to pinpoint the exact cause:
async function debugInvalidArgument(token, message) {
try {
const response = await admin.messaging().send({
token,
...message
});
console.log('Send successful:', response);
} catch (error) {
console.error('FCM Error Details:');
console.error(' Code:', error.code); // messaging/invalid-argument
console.error(' Message:', error.message); // Full error message
console.error(' Full error:', JSON.stringify(error, null, 2));
// Check error.errorInfo for FCM-specific details
if (error.errorInfo) {
console.error(' Error Info:', error.errorInfo);
}
// Response from HTTP v1 API includes details array
if (error.cause && error.cause.response) {
console.error(' Response status:', error.cause.response.status);
console.error(' Response data:', error.cause.response.data);
const { details } = error.cause.response.data.error;
if (details && details.length > 0) {
console.error(' FCM details:', details);
details.forEach(detail => {
console.error(' -', detail['@type'], ':', detail);
});
}
}
// Analyze the error message
const errorMsg = error.message || '';
if (errorMsg.includes('registration token')) {
console.error('ROOT CAUSE: Invalid registration token');
} else if (errorMsg.includes('data too large')) {
console.error('ROOT CAUSE: Message payload exceeds limits');
} else if (errorMsg.includes('1000')) {
console.error('ROOT CAUSE: Batch size exceeds 1000 tokens');
} else if (errorMsg.includes('priority') || errorMsg.includes('high')) {
console.error('ROOT CAUSE: Invalid priority for message type/platform');
}
}
}
// Test with sample data
const testToken = 'your-test-token-here';
const testMessage = {
notification: { title: 'Test', body: 'Message' }
};
await debugInvalidArgument(testToken, testMessage);The error message often contains hints:
- "registration token" → invalid/expired token
- "data too large" → payload exceeds 4096 bytes
- "1000" → batch too large
- "priority" → incorrect priority for platform
- "APNs" → iOS-specific validation issue
Advanced notes
### Firebase Cloud Messaging Validation Rules
Token Validation:
- Tokens must be 100-152 characters
- Tokens expire after 270 days of inactivity on Android
- iOS tokens can become invalid if app is uninstalled
- Web tokens expire based on service worker registration status
- Testing tokens (if using FCM emulator) have different formats
Payload Limits:
- Total message: 4096 bytes maximum
- Notification object: 4000 bytes maximum
- Data object: 4000 bytes maximum
- Single data value: no hard limit but keep under 1000 characters
- Notification title: 240 characters recommended maximum
Platform-Specific Requirements:
- iOS (APNs): High priority (apns-priority: 10) requires notification, silent notifications use normal priority
- Android: High priority bypasses doze mode, triggers immediate delivery
- Web (Webpush): TTL header controls message retention (in seconds)
- Multiple platforms: Each platform gets its own config to avoid INVALID_ARGUMENT
Message Structure Requirements:
- Must have either token, topic, or condition
- tokens array for sendMulticast (max 500-1000 tokens)
- condition for topic-based or complex logical conditions
- Message must include at least notification OR data (not empty)
### Common Patterns by Platform
Android Specific:
const androidMessage = {
token: androidToken,
notification: {
title: 'Title',
body: 'Body',
tag: 'notification-tag',
color: '#ff0000'
},
android: {
priority: 'high', // high or normal
ttl: 3600,
restrictedPackageName: 'com.your.app'
}
};iOS Specific:
const iosMessage = {
token: iosToken,
notification: {
title: 'Title',
body: 'Body'
},
apns: {
headers: {
'apns-priority': '10', // 10 for alert, 5 for background
'apns-expiration': String(Math.floor(Date.now() / 1000) + 3600)
},
payload: {
aps: {
alert: {
title: 'Title',
body: 'Body'
},
sound: 'default',
'content-available': 0 // 1 for silent notifications
}
}
}
};Web Specific:
const webMessage = {
token: webToken,
notification: {
title: 'Title',
body: 'Body',
icon: 'https://example.com/icon.png'
},
webpush: {
headers: {
TTL: '3600' // Seconds to retain message
},
data: {
url: 'https://example.com/page'
}
}
};### Error Recovery Patterns
Implement exponential backoff for transient failures:
async function sendWithRetry(token, message, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await admin.messaging().send({ token, ...message });
} catch (error) {
if (error.code === 'messaging/invalid-argument') {
// Don't retry invalid argument errors
throw error;
}
const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
console.log(`Attempt ${attempt + 1} failed, retrying in ${delay}ms`);
await new Promise(r => setTimeout(r, delay));
}
}
throw new Error('Max retries exceeded');
}Monitor and alert on INVALID_ARGUMENT spikes:
const errorMetrics = {
'messaging/invalid-argument': 0,
'messaging/registration-token-not-registered': 0,
'messaging/third-party-auth-error': 0
};
async function trackSendError(error) {
const errorCode = error.code;
if (errorCode in errorMetrics) {
errorMetrics[errorCode]++;
// Alert if error rate is too high
if (errorMetrics[errorCode] > 100) {
console.warn(`High error rate for ${errorCode}`);
// Send alert, page on-call, etc.
}
}
}### Testing Strategies
Use Firebase Emulator Suite for local testing without risking real tokens:
firebase emulators:start --only messagingThis helps identify invalid-argument issues before production deployment.
Frequently asked questions
- What does "messaging/invalid-argument" mean?
- The "messaging/invalid-argument" error in Firebase Cloud Messaging (FCM) is returned by the FCM service when a message request violates one of FCM's validation rules. Unlike client-side parameter errors, this error comes from the FCM API server after validating the complete request. This error can indicate several different problems depending on context: invalid device registration tokens, message payloads that exceed size limits, sending to more than 1000 tokens in a single request, incorrect message structure for the target platform, or invalid priority settings for specific device types.
- How do I fix "messaging/invalid-argument"?
- The Firebase Cloud Messaging "messaging/invalid-argument" error occurs when FCM rejects a send request due to invalid parameters, malformed tokens, oversized payloads, or incorrect message structure. This is a critical error that prevents messages from being delivered to devices.
- What causes "messaging/invalid-argument"?
- Registration token is invalid, expired, or malformed (most common) Batch send exceeds 1000 device tokens per request (FCM maximum) Message payload exceeds maximum size (4096 bytes total, 4000 bytes for notifications)
- Is the "messaging/invalid-argument" error serious?
- Yes. This is a high-severity error that usually blocks work until it is fixed, but the steps above resolve it.
Official resources & further reading
- 📚Firebase Official Documentation
- 📖FCM Error Codes - Firebase Cloud Messaging Documentation(Official Docs)
- 📖Best practices for FCM registration token management(Official Docs)
- 📖Set the priority of a message - Firebase Cloud Messaging(Official Docs)
- 🐙