Issue
Summary
On iOS, messaging().onMessage() is never called for a foreground push whose APNs payload explicitly sets aps.content-available: 0.
- The same push is delivered and displayed correctly in the background and quit states, and tapping it works.
- The same app receives
onMessage normally for foreground pushes that omit the content-available key.
- Nothing arrives at JS at all: no event, no error, nothing in the notification center either (since the app never gets a chance to present it).
content-available: 0 is valid APNs usage — it is an explicit declaration that the push is not a silent push. Any sender that builds its own aps dictionary and serializes that field as 0 hits this. We first saw it after integrating a third‑party marketing push provider that sets mutable-content: 1 and assembles apns.payload.aps itself; our own backend pushes (FCM notification block only, no content-available key) were unaffected, which is why it went unnoticed.
Usage
import { getMessaging, onMessage } from '@react-native-firebase/messaging';
useEffect(() => {
const unsubscribe = onMessage(getMessaging(), async remoteMessage => {
console.log('foreground message', remoteMessage); // never logged for content-available: 0
});
return unsubscribe;
}, []);
Reproduction
Send two foreground pushes to the same iOS device (physical device — the simulator cannot receive remote pushes) and observe onMessage:
| # |
Payload |
onMessage |
| 1 |
{"aps":{"alert":{"title":"a","body":"b"}}} |
fires ✅ |
| 2 |
{"aps":{"alert":{"title":"a","body":"b"},"content-available":0}} |
never fires ❌ |
Both payloads carry gcm.message_id, so both pass the outer guard in willPresentNotification; only the inner contentAvailable check differs.
Via FCM HTTP v1 the second payload is produced by:
{
"message": {
"token": "<device token>",
"apns": {
"payload": {
"aps": {
"alert": { "title": "a", "body": "b" },
"content-available": 0
}
}
}
}
}
Root cause
RNFBMessagingSerializer stores contentAvailable whenever the key is present, regardless of its value:
// packages/messaging/ios/RNFBMessaging/RNFBMessagingSerializer.m
// message.contentAvailable
if (apsDict[@"content-available"] != nil) {
message[@"contentAvailable"] = @([RCTConvert BOOL:apsDict[@"content-available"]]);
}
// message.mutableContent <- note: this one compares the value
if (apsDict[@"mutable-content"] != nil && [apsDict[@"mutable-content"] intValue] == 1) {
So content-available: 0 yields @(NO) — a non-nil NSNumber.
willPresentNotification then guards event emission on key presence rather than on the value:
// packages/messaging/ios/RNFBMessaging/RNFBMessaging+UNUserNotificationCenter.m:122
// Don't send an event if contentAvailable is true -
// application:didReceiveRemoteNotification will send the event for us, we
// don't want to duplicate them
if (!notificationDict[@"contentAvailable"]) {
[[RNFBRCTEventEmitter shared] sendEventWithName:@"messaging_message_received"
body:notificationDict];
}
@0 is a non-nil object, so !notificationDict[@"contentAvailable"] is false and messaging_message_received is never emitted. The comment directly above states the intent in terms of the value ("if contentAvailable is true"), so this is an implementation/intent mismatch rather than deliberate behaviour.
This guard is older than PR #5604 — that PR actually removed it and it was restored by the revert in #5641 (Aug 2021). It has not been revisited since, including through the willPresentNotification changes this year (#8786, #9094, #8945). The code path is unchanged on main at the time of writing.
Fix
Compare the value, matching the stated intent. One line:
- if (!notificationDict[@"contentAvailable"]) {
+ if (![notificationDict[@"contentAvailable"] boolValue]) {
Genuine silent pushes (content-available: 1) are still skipped, preserving the de‑duplication with application:didReceiveRemoteNotification:.
PR with this one-line fix follows (linked below).
Verified on a physical device before/after the change by streaming the native delegate with xcrun devicectl device process launch --console: before, the content-available: 0 payload reaches willPresentNotification but no event is emitted; after, onMessage fires and the serialized message carries "contentAvailable": false — confirming the serializer had been producing a correct dictionary all along and only the emission guard was wrong.
Workaround (until released)
Patch the one line above with patch-package / pnpm patchedDependencies. There is no JS-side workaround: the decision not to emit lives inside the native bridge, so onMessage subscribers never see an event that was never sent.
Possibly related
Same symptom string, but those threads never identified a payload-level cause and some describe a different failure mode (provisional authorization / token registration timing), so I am not claiming this explains them: #7772, #6107, #4513.
Project Files
Javascript
Click To Expand
package.json:
{
"dependencies": {
"react": "19.0.0",
"react-native": "0.78.x",
"@react-native-firebase/app": "19.3.0",
"@react-native-firebase/messaging": "19.3.0"
}
}
(Trimmed to the relevant packages. The bug is independent of app configuration — it is a payload-value check in the native bridge, reproducible with the two payloads above alone.)
firebase.json for react-native-firebase v6:
# N/A — defaults; no messaging-related keys set
iOS
Click To Expand
ios/Podfile:
# Standard RN 0.78 Podfile; use_frameworks! :linkage => :static
# No RNFB-specific overrides. Not relevant to this issue (native bridge value check).
AppDelegate.m:
// Standard RN 0.78 AppDelegate (Swift). No custom UNUserNotificationCenterDelegate —
// RNFBMessaging's own delegate is the one handling willPresentNotification.
Android
Click To Expand
Have you converted to AndroidX?
android/build.gradle:
// N/A — iOS-only issue (the affected code is in the iOS bridge)
android/app/build.gradle:
android/settings.gradle:
MainApplication.java:
AndroidManifest.xml:
Environment
Click To Expand
react-native info output:
System:
OS: macOS 26.x
Binaries:
Node: 22.x
IDEs:
Xcode: 26.x
npmPackages:
react: 19.0.0
react-native: 0.78.x
Device: iPhone (physical), iOS 26.5.1
JS engine: JSC, old architecture (newArchEnabled=false) — not relevant, the guard runs before the bridge
- Platform that you're experiencing the issue on:
react-native-firebase version you're using that has this issue:
19.3.0 (reproduced); the affected code is unchanged on main (26.x)
Firebase module(s) you're using that has the issue:
- Are you using
TypeScript?
Issue
Summary
On iOS,
messaging().onMessage()is never called for a foreground push whose APNs payload explicitly setsaps.content-available: 0.onMessagenormally for foreground pushes that omit thecontent-availablekey.content-available: 0is valid APNs usage — it is an explicit declaration that the push is not a silent push. Any sender that builds its ownapsdictionary and serializes that field as0hits this. We first saw it after integrating a third‑party marketing push provider that setsmutable-content: 1and assemblesapns.payload.apsitself; our own backend pushes (FCMnotificationblock only, nocontent-availablekey) were unaffected, which is why it went unnoticed.Usage
Reproduction
Send two foreground pushes to the same iOS device (physical device — the simulator cannot receive remote pushes) and observe
onMessage:onMessage{"aps":{"alert":{"title":"a","body":"b"}}}{"aps":{"alert":{"title":"a","body":"b"},"content-available":0}}Both payloads carry
gcm.message_id, so both pass the outer guard inwillPresentNotification; only the innercontentAvailablecheck differs.Via FCM HTTP v1 the second payload is produced by:
{ "message": { "token": "<device token>", "apns": { "payload": { "aps": { "alert": { "title": "a", "body": "b" }, "content-available": 0 } } } } }Root cause
RNFBMessagingSerializerstorescontentAvailablewhenever the key is present, regardless of its value:So
content-available: 0yields@(NO)— a non-nilNSNumber.willPresentNotificationthen guards event emission on key presence rather than on the value:@0is a non-nil object, so!notificationDict[@"contentAvailable"]is false andmessaging_message_receivedis never emitted. The comment directly above states the intent in terms of the value ("if contentAvailable is true"), so this is an implementation/intent mismatch rather than deliberate behaviour.This guard is older than PR #5604 — that PR actually removed it and it was restored by the revert in #5641 (Aug 2021). It has not been revisited since, including through the
willPresentNotificationchanges this year (#8786, #9094, #8945). The code path is unchanged onmainat the time of writing.Fix
Compare the value, matching the stated intent. One line:
Genuine silent pushes (
content-available: 1) are still skipped, preserving the de‑duplication withapplication:didReceiveRemoteNotification:.PR with this one-line fix follows (linked below).
Verified on a physical device before/after the change by streaming the native delegate with
xcrun devicectl device process launch --console: before, thecontent-available: 0payload reacheswillPresentNotificationbut no event is emitted; after,onMessagefires and the serialized message carries"contentAvailable": false— confirming the serializer had been producing a correct dictionary all along and only the emission guard was wrong.Workaround (until released)
Patch the one line above with
patch-package/ pnpmpatchedDependencies. There is no JS-side workaround: the decision not to emit lives inside the native bridge, soonMessagesubscribers never see an event that was never sent.Possibly related
Same symptom string, but those threads never identified a payload-level cause and some describe a different failure mode (provisional authorization / token registration timing), so I am not claiming this explains them: #7772, #6107, #4513.
Project Files
Javascript
Click To Expand
package.json:{ "dependencies": { "react": "19.0.0", "react-native": "0.78.x", "@react-native-firebase/app": "19.3.0", "@react-native-firebase/messaging": "19.3.0" } }(Trimmed to the relevant packages. The bug is independent of app configuration — it is a payload-value check in the native bridge, reproducible with the two payloads above alone.)
firebase.jsonfor react-native-firebase v6:# N/A — defaults; no messaging-related keys setiOS
Click To Expand
ios/Podfile:AppDelegate.m:Android
Click To Expand
Have you converted to AndroidX?
android/gradle.settingsjetifier=truefor Android compatibility?jetifierfor react-native compatibility?android/build.gradle:// N/A — iOS-only issue (the affected code is in the iOS bridge)android/app/build.gradle:// N/Aandroid/settings.gradle:// N/AMainApplication.java:// N/AAndroidManifest.xml:<!-- N/A -->Environment
Click To Expand
react-native infooutput:react-native-firebaseversion you're using that has this issue:19.3.0(reproduced); the affected code is unchanged onmain(26.x)Firebasemodule(s) you're using that has the issue:MessagingTypeScript?Y&5.xReact Native FirebaseandInvertaseon Twitter for updates on the library.