Diagnosing Expo Push Notification Failures in Managed Workflow Apps
Step‑by‑step guide to diagnose why Expo push notifications aren’t appearing, with checks for tokens, listeners, credentials, and OTA updates, plus fixes and when to escalate.
22 Nov 2025, 16:24 UTC

Recognizable condition
Users report that push notifications are never displayed on iOS or Android devices, even though the backend sends a valid payload and the Expo push token appears to be correct. The app may be in the managed workflow (Expo Go or a development client) or a custom build produced with expo build.
Cause / diagnostic table
| Symptom | Possible cause |
|---|---|
| No notification received | Token not registered, expired, or not sent to backend |
| Notification received but not displayed | Missing listener or incorrect handling of incoming notification |
| Android silent notifications | Missing Firebase project ID or incorrect google-services.json |
| iOS silent notifications | APNs key not uploaded or misconfigured in Expo dashboard |
| Token disappears after OTA update | Token stored in AsyncStorage cleared by the update |
Ordered checks
- Verify token acquisition and storage
Run the app on a device or emulator, then execute:
expo push:token --deviceWhere to run: terminal, inside the Expo project directory. No special permissions are needed beyond having the Expo CLI installed. The command prints a token like
ExponentPushToken[xxxxxxxxxxxxxxxxxxxx]. Check that this token matches the one stored on your backend. If the token is missing or changes on every launch, the app may not be callingNotifications.getExpoPushTokenAsync()on start.Risk: Accidentally logging the token to a public service could expose it; only use trusted logs.
- Confirm
expo-notificationssetupEnsure the package is installed (
expo install expo-notifications) and that the app calls:Notifications.setNotificationHandler({ handleNotification: async () => ({ shouldShowAlert: true, shouldPlaySound: true, shouldSetBadge: false }) })await Notifications.requestPermissionsAsync()early in the app lifecycle.
If either step is missing, the token may be valid but the OS will silently drop the notification.
- Check native credentials in Expo dashboard
For Android: upload a valid
google-services.json(generated from Firebase console) under Project Settings → Credentials. For iOS: upload an APNs authentication key (.p8) under the same section. Missing or outdated credentials cause silent failures on the respective platform. - Inspect device logs for
expo-notificationserrorsAndroid: run
adb logcat | grep ExpoNotificationswhile reproducing the issue. iOS: open Xcode → Devices and Simulators → View Device Logs, filter forExpoNotifications. Look for lines such asFailed to register for push notificationsorInvalid APNs key. - Run
expo doctorThis command scans the project for common configuration problems. Pay attention to any warnings under the "Notifications" section, such as missing credentials or outdated SDK version.
- Validate OTA update and token persistence
If you suspect the token is cleared after an OTA push, persist it with
expo-secure-storeor re‑fetchgetExpoPushTokenAsync()after each update and send it to your backend.
Fixes tied to findings
- Token missing or expired – Add
Notifications.getExpoPushTokenAsync()to your app’s root component (e.g., inuseEffect) and transmit the returned token to your server. Store it securely (e.g., SecureStore) and refresh it on every app start. - Listener not handling incoming notifications – Implement
Notifications.addNotificationReceivedListener(and optionallyaddNotificationResponseReceivedListener) to trigger a local notification, update UI, or log the event. Example:
const subscription = Notifications.addNotificationReceivedListener(notification => {
console.log('Received:', notification);
// Optionally show a local alert or update Redux state
});
// Remember to remove subscription on unmount
- Android credentials wrong – Regenerate
google-services.jsonfrom Firebase → Project Settings → General → Your apps → Android app → Downloadgoogle-services.json. Place it at the project root and rebuild the native binary withexpo prebuild && expo run:android(or create a new build viaexpo build:android). - iOS APNs missing – In the Expo dashboard, go to Project Settings → Credentials → Push Notifications → Upload APNs Auth Key. Provide the key ID, team ID, and the .p8 file downloaded from Apple Developer Account → Certificates, IDs & Profiles → Keys.
- OTA cleared token – Replace AsyncStorage with
expo-secure-storeor callgetExpoPushTokenAsync()after each OTA update and push the fresh token to your backend.
After applying a fix, rebuild the native binary (if you changed credentials) and publish a new OTA update with expo publish. Then repeat the verification steps.
Escalation criteria
- If notifications still fail after completing all checks and applying the relevant fixes, verify the backend is sending to the exact token returned by
expo push:token --deviceand that the Expo push service returns a successful receipt (HTTP 200 with areceiptId). - Collect full device logs (Android via
adb logcat, iOS via Xcode console) and the output ofexpo doctorandexpo push:token --device. Post these logs to the Expo forums or open a support ticket, attaching the logs. - Consider ejecting to the bare workflow (
expo eject) only if the problem persists and you suspect a native module misconfiguration that cannot be resolved through Expo’s credential management.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.