Skip to main content

Troubleshooting

Server

SymptomLikely causeFix
API exits immediately at bootRedis unreachableThe API refuses to start without a queue. Check REDIS_URL; docker compose ps redis.
/health returns 503, API still upMongoDB unreachableIntentional — it retries in the background. Check MONGO_URL, the mongo container.
docker compose up fails with set ENCRYPTION_KEY in .envSecrets missingbun run generate:keys >> .env and remove the empty placeholders.
/v1/admin/* answers 404ADMIN_API_TOKEN unset or the dashboard's copy differsSet the same value on both; restart the API.
Dashboard: ADMIN_PASSWORD_HASH is not set or login always failsHash pasted raw ($argon2id…) and a .env parser expanded the $ signsPaste the base64 line exactly as generate:admin-password printed it.
Dashboard login works, then every page redirects to loginCookie dropped over plain HTTPINSECURE_COOKIES=true for localhost; HTTPS in production.
Campaign stuck in sendingWorker not running, or a platform with no credentialsdocker compose logs worker; check the report's failure table.
Scheduled campaign never firedRedis restarted without persistenceCompose sets appendonly yes; if you run your own Redis, enable AOF or RDB.
Queue grows, Redis memory climbsWorker down / starved, or allkeys-lruStart workers; policy must be noeviction.
Every push on one platform fails with InvalidProviderToken / PERMISSION_DENIEDWrong or revoked credentialRe-upload; press Test connection.
SENDER_ID_MISMATCH on many Android devicesTokens from a different Firebase project than the service accountUse the service account of the project the app's google-services.json belongs to.
Rate limited (429) on subscribe during an importPer-IP cap of 10/minRaise SUBSCRIBE_RATE_LIMIT temporarily or run the import from the API host.

iOS

SymptomLikely causeFix
Permission granted, device never appears in the dashboarddidRegisterForRemoteNotificationsWithDeviceToken not forwardedAdd the AppDelegate callback (Swift, RN).
BadDeviceToken for every sendSandbox/production mismatchDebug builds → Production APNs off; App Store/TestFlight → on. Or two apps, one per environment.
DeviceTokenNotForTopicBundle ID on the credentials page differs from the app'sMatch them exactly.
TopicDisallowed / 403 on testApp ID lacks the Push Notifications capability, or key made without APNsEnable the capability in the portal; recreate the key with APNs ticked.
Delivered stays at 0 %, opens still countNo Notification Service ExtensionAdd the NSE target with the template and OpenNotificationApiUrl.
Image never showsSame — NSE missing, or the image URL is not HTTPS
Buttons do not appearNSE missing (it registers the category)
Notification shows nothing while the app is openwillPresent not forwardedForward it and complete with [.banner, .sound, .badge].
Works on device, not on simulatorSimulator has no real APNs tokenTest on a device.

Android

SymptomLikely causeFix
No token / device never registersgoogle-services.json missing or wrong package name; plugin not appliedCompare applicationId with the Firebase app; apply com.google.gms.google-services.
Grey square instead of an iconColoured or non-transparent small iconMonochrome white-on-transparent ic_notification.png.
No notification on Android 13+POST_NOTIFICATIONS not grantedCall requestPermission(activity); declare the permission.
Arrives with app open, not in backgroundRN: the native module is not linked (old build)Rebuild the Android app so autolinking picks up @opennotification/react-native/android.
Delivered when app is open, silent when force-stoppedOS behaviour for data messages on force-stopped apps / OEM battery saverExpected; priority: high is already set. Ask users to exempt the app from battery optimisation on Xiaomi/Oppo/vivo.
Two FirebaseMessagingServicesAnother SDK also registers oneForward onMessageReceived to OpenNotificationMessagingService.handle.
UNREGISTERED immediately after installApp reinstalled; old token invalidatedNormal — the new token subscribes on next launch.

Web

SymptomLikely causeFix
capability().reason === "IOS_NEEDS_INSTALL"Safari tab on iOSShow the install guide; push only works from the Home Screen app.
INSECURE_CONTEXTSite over HTTPHTTPS (localhost is exempt).
permission_dismissed every timesubscribe() not called from a clickCall it in the click handler, synchronously after the gesture.
service_worker_failed/sw.js 404 or served from the wrong scopeServe it at the root with Content-Type: application/javascript.
api_error 401/403 on subscribeWrong key or CORSUse the pk_ key; add the origin to CORS_ORIGINS.
Subscribed, nothing arrivesVAPID public key in the SDK differs from the app'sCopy the key from the credentials page.
413 failuresPayload over 4 KBTrim body/data; the wizard's byte meter shows it.
Subscriptions die after a while (410)Browser rotated the endpoint and the old worker did not handle pushsubscriptionchangeUse the shipped worker or registerPushHandlers.
Safari: no image, no buttonsUnsupported on SafariExpected.

"It says sent but nobody got it"

sent means the push service accepted it — the last thing the server can see. From there: the device may be offline (TTL still counting), the app force-stopped (Android), notifications disabled in system settings, or, on iOS, the app was deleted (the next send returns Unregistered and cleans up). The delivered count is the truth; compare it per platform in the report.

Getting more detail

docker compose logs -f worker | grep -E "failed|invalidated"

Logs carry subscriptionId and the provider code, never the token. Look the subscription up in the dashboard for the device facts.