Skip to content

FCM says success but the notification never arrives

Why Android does not show FCM notifications when the app is closed or killed: message types, priority, Doze, permissions, stale tokens and OEM battery savers.

PushUpdated 19 October 20268 min read

FCM answered 200 with a message id, and the phone stayed silent. That response only means Google accepted the message. Between FCM and the screen there are six places it can stop. Work through them in this order; the first two explain most cases.

1. A data-only message while the app is killed

FCM has two kinds of message:

  • Notification messages have a notification block. When your app is in the background or closed, Android’s system tray shows them without running any of your code.
  • Data messages have only a data block. Android always hands them to your app’s onMessageReceived. If your app is not running and cannot be started, nothing is shown.

If your app builds the notification itself from data, it works while the app is open and fails when it is closed. The fix is to include a notification block for anything the user must see:

{
  "message": {
    "token": "DEVICE_TOKEN",
    "notification": { "title": "Sign in from Chrome?", "body": "Tap to approve" },
    "data": { "challenge_id": "apr_51mz0c" },
    "android": { "priority": "high", "notification": { "channel_id": "logins" } }
  }
}

2. The notification permission on Android 13 and later

From Android 13 (API 33), apps must ask for POST_NOTIFICATIONS at run time. Without it, FCM still delivers and returns success, and Android silently drops the notification. Ask at a moment the user understands, like right after they turn on order updates, not on the first screen.

3. Priority and Doze

When a phone sits idle, Android’s Doze mode holds normal-priority messages until a maintenance window, which can be many minutes later. High-priority messages wake the device. Set android.priority to high for messages the user is waiting for. Do not make everything high priority: Android may lower the priority of apps whose high-priority messages do not result in a visible notification.

4. A channel the user muted

On Android 8 and later every notification goes through a channel. If you send to a channel id that does not exist, or the user turned the channel off, nothing appears. Create your channels at app start and keep them separate, for example logins and offers, so users can mute offers without missing logins.

5. A stale or wrong token

Tokens change when the app is reinstalled, its data is cleared, or it is restored on a new phone. A token from your development build will not work with your production Firebase project either. Send to a dead token and v1 returns UNREGISTERED; delete it. Refresh the token in your app with onNewToken and send the new one to your server every time.

6. The phone maker’s battery saver

Xiaomi, Redmi, Poco, Oppo, Realme, Vivo and OnePlus phones add their own battery management on top of Android. Swiping an app away from recents can act like a force stop, and Android does not deliver messages to a force-stopped app until the user opens it again. This is the most common reason pushes fail in India, where these brands sell most of the phones. The fixes are in push notifications on Xiaomi, Oppo, Vivo and Realme.

A quick test that separates the causes

  1. Send a notification message to one token from the Firebase console’s test send, with the app in the background, not swiped away.
  2. If that shows, your message type or channel is the problem.
  3. If it does not, check the permission, then try the same test on a Pixel or Samsung. If it shows there and not on a Xiaomi, it is the battery saver.

What SignOps does about it

SignOps Push sends notification messages with high priority only where the user expects them, keeps separate channels for approvals and offers, removes tokens FCM reports as dead, and gives you a delivery log per device. For Approve, the SDK also fetches pending requests when the app opens, so an approval held by a battery saver still reaches the user.

Sources: Firebase: receive messages in an Android app, Android: notification runtime permission, Android: optimize for Doze and App Standby.

Frequently asked questions

More questions? Email us and the answer goes on this page.

hello@signops.dev
Why does FCM return success but the device shows nothing?

A success response means FCM accepted the message, not that the phone displayed it. The usual reasons it does not show are a data-only message while the app is killed, a missing notification permission on Android 13 and later, a muted notification channel, a stale token, or a phone maker's battery saver stopping the app.

Why do notifications work when the app is open but not when it is closed?

With a data-only message, Android hands the message to your app's code, and if the app was swiped away on some phones or force-stopped, that code never runs. Send a notification message, which the system tray shows without your code, or a data message with high priority.

Does a force-stopped app receive FCM messages?

No. Android does not deliver broadcasts, including FCM, to an app the user force-stopped in Settings until the user opens it again. Some phone makers treat swiping an app away like a force stop.

Should every message be high priority?

No. High priority wakes the phone from Doze, and Android may lower the priority of apps that send high-priority messages without showing a notification. Use high priority for messages the user is waiting for, like a login approval or a delivery update.

Send your first push today.

Free for 1,000 push devices, 100 approvals a day, 10,000 passkey sign-ins a month and 500 email codes to try. No card until you pass a free amount.