Skip to content

Expo push notification errors, and what to do about each

Every Expo push ticket, receipt and request error: DeviceNotRegistered, MessageTooBig, MismatchSenderId, InvalidCredentials, TOO_MANY_REQUESTS, with fixes.

PushBy Rushi Patel, Senior iOS developerUpdated 2 November 20266 min read

Expo’s push service answers in two stages. The ticket comes back at once and says whether Expo accepted the message. The receipt, fetched later with the ticket’s id, says what Apple or Google did with it. Errors can appear in either, and a whole request can also fail. Here is every code Expo documents, and what to do about each.

Request errors (the whole call failed)

These come back in the response’s top-level errors array.

Code Cause Fix
PUSH_TOO_MANY_NOTIFICATIONS More than 100 messages in one request Send in batches of 100 or fewer
TOO_MANY_REQUESTS Over 600 notifications a second for the project Throttle; the Node SDK does this
PUSH_TOO_MANY_EXPERIENCE_IDS Tokens from more than one Expo project in one request Group tokens by project; details lists which belong where
PUSH_TOO_MANY_RECEIPTS More than 1,000 ids in one receipts request Ask for 1,000 or fewer at a time
UNAUTHORIZED Push security is on and no valid access token was sent Send Authorization: Bearer <token>, or accessToken in expo-server-sdk 3.6.0 and later

Ticket errors

A ticket with "status": "error" failed before leaving Expo.

details.error Meaning Fix
DeviceNotRegistered The device can no longer get pushes Delete the token

Receipt errors

Fetch receipts with the ticket ids:

const res = await fetch("https://exp.host/--/api/v2/push/getReceipts", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ ids: ticketIds }),
});
const { data } = await res.json();
for (const [id, r] of Object.entries(data)) {
  if (r.status === "error") console.log(id, r.details?.error, r.message);
}
details.error Meaning Fix
DeviceNotRegistered Apple or Google says the device is unregistered: app uninstalled, notifications revoked Delete the token until the app registers again
MessageTooBig The payload is over 4,096 bytes Send less: an id, then fetch the rest in the app
MessageRateExceeded Too many messages to one device in a short time Back off exponentially and send more slowly to that device
MismatchSenderId The FCM credentials and the google-services.json sender id are from different Firebase projects Use one Firebase project for both
InvalidCredentials Push credentials for your standalone app are invalid or revoked Android: upload the FCM V1 service account key again. iOS: eas credentials; rebuild if the message mentions InvalidProviderToken

Receipts are kept 24 hours. Expo suggests checking them about 15 minutes after sending; a receipt still missing then points to a problem on Expo’s side.

A sender that handles all of it

With the official Node SDK, chunking, throttling and receipts look like this:

import { Expo } from "expo-server-sdk";
const expo = new Expo({ accessToken: process.env.EXPO_ACCESS_TOKEN });

const messages = tokens.filter(Expo.isExpoPushToken).map((to) => ({ to, title, body, data }));
const tickets = [];
for (const chunk of expo.chunkPushNotifications(messages)) tickets.push(...(await expo.sendPushNotificationsAsync(chunk)));

const dead = [];
tickets.forEach((t, i) => { if (t.status === "error" && t.details?.error === "DeviceNotRegistered") dead.push(messages[i].to); });

// 15 minutes later, from a job:
const ids = tickets.filter((t) => t.status === "ok").map((t) => t.id);
for (const chunk of expo.chunkPushNotificationReceiptIds(ids)) {
  const receipts = await expo.getPushNotificationReceiptsAsync(chunk);
  // handle DeviceNotRegistered, MessageTooBig, InvalidCredentials as in the table
}

Keep the token with each ticket id when you store them, so a receipt error can point back to the token to delete.

When there is no error at all

Ticket ok, receipt ok, nothing on the phone: Apple or Google accepted it and the device did not show it. On Android that is usually the notification permission on Android 13 and later, a missing channel, or a phone maker’s battery saver. See Expo push notifications not working, the Android 13 notification permission and push on Xiaomi, Oppo and Vivo.

Sending without Expo’s service

Expo’s service is free and works well for Expo apps. If you want one sender for Expo, bare React Native, Flutter, native apps and the web, SignOps Push takes your FCM and APNs credentials directly, answers a push to one user with how many of their devices it reached and how many failed, with no receipts to fetch, and removes dead tokens itself. Setup: push in React Native, Expo and Flutter.

Sources: Expo: Send notifications with the Expo Push Service, expo-server-sdk-node.

Frequently asked questions

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

hello@signops.dev
What does DeviceNotRegistered mean in Expo push?

Apple or Google says the device can no longer receive pushes for your app, usually because the app was uninstalled or notifications were revoked. Stop sending to that Expo push token and delete it, until the app registers again and sends you a token.

How do I fix InvalidCredentials in Expo push notifications?

The push credentials Expo holds for your standalone app are invalid or revoked. On Android, upload your FCM V1 service account key again under the project's credentials. On iOS, run eas credentials to regenerate the push key; if the details mention InvalidProviderToken, a rebuild may be needed.

Why does the Expo push ticket say ok but the notification never arrives?

A ticket only means Expo accepted the message. Errors from Apple and Google appear in the push receipt, which you fetch with the ticket id from /--/api/v2/push/getReceipts. Check receipts about 15 minutes after sending; Expo clears them after 24 hours.

How many push notifications can I send to Expo at once?

Up to 100 messages per request and 600 notifications a second per project. Over 100 in one request returns PUSH_TOO_MANY_NOTIFICATIONS; over 600 a second returns TOO_MANY_REQUESTS. The expo-server-sdk for Node chunks and throttles for you.

Send your first push today.

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