Skip to main content
Version: 1.2

18. Troubleshooting

18.1 "A user isn't receiving notifications at all"

Debug checklist, in order:

  1. Is there a NotificationEventConfiguration row for the event's EventKey, and is the relevant channel's Enable{Channel} flag true? No row at all means all channels are enabled by default — a row that exists but has the flag off is the more common cause. See §9.4.
  2. Does an AppNotificationTemplate row exist for that EventKey + Channel + the recipient's language (or a LanguageCode = null fallback)? Missing template = silent skip, no error. See Chapter 8.
  3. Check NotificationDeliveryAttempt for the EventKey/recipient — was an attempt even made? If nothing appears, the dispatch call itself may not be happening (a bug in the calling business service) rather than a delivery failure.
  4. If an attempt exists with Succeeded = false, read ErrorCode/ErrorMessage — this usually points straight at the root cause (bad recipient, provider auth failure, etc.).
  5. For Push specifically: does the recipient have an active DeviceToken row (GetUserDevices/{userId}, §9.3)? No device tokens means Push is silently a no-op for that user.
  6. For SignalR specifically: was the recipient connected at dispatch time? SignalR has no redelivery — check the InApp channel / GetInbox instead for durable confirmation.

18.2 "WhatsApp messages aren't sending"

Walk the guard chain in §7.3 top to bottom — each guard logs a specific LogInformation/LogWarning line naming exactly which condition failed:

  • EnableWhatsApp/EnableWhatsAppNotifications false → "WhatsApp notification dispatch skipped: disabled"
  • EnableWhatsAppTemplates false → "WhatsApp template dispatch skipped: EnableWhatsAppTemplates=false"
  • Missing WhatsAppTemplateName on the template → "WhatsApp dispatch skipped: template has no WhatsAppTemplateName"
  • Missing AccessToken/PhoneNumberId in configuration → "WhatsApp dispatch skipped: missing AccessToken or PhoneNumberId"
  • Phone normalization failure → "WhatsApp dispatch skipped: invalid phone"

Search application logs for these exact phrases (they include EventKey in the log context) to find the precise guard that tripped, rather than guessing from the outside.

18.3 "WhatsApp delivery receipts never update"

Confirm WhatsAppCloudApi.SignatureValidationEnabled is true and that AppSecret matches what's registered in the Meta app — a signature mismatch causes the inbound webhook to reject the callback silently from the caller's perspective (Meta sees a failure and will retry/eventually stop). Also confirm the receipt-mapping call (INotificationDeliveryReceiptService.UpsertAsync) isn't throwing — it's wrapped in a try/catch so a mapping bug won't break the pre-existing ProcessWebhookStatusAsync logic, but it also means a silent receipt-mapping failure won't surface as an error anywhere obvious; check logs for the receipt-mapper's warning output specifically.

18.4 "A notification is stuck in the retry queue and never resolves"

Check NotificationRetryQueue.ProcessingToken — if non-null and State = Retrying for more than 2 hours, it should be picked up by the retry-expire job's orphan repair (hourly). If it's older than that and still stuck, the notification-retry-expire-pipeline job itself may not be running — check the Hangfire dashboard for its last execution time (see §16.3).

18.5 "Analytics numbers look wrong / don't match expectations"

Confirm the date range being queried — most analytics endpoints default to last 7 days and cap at 90 days (§9.8) if no explicit dateFrom/dateTo is passed; a query intended to cover a longer historical window will silently be truncated to 90 days.

18.6 "The server checklist's ResultState numbers don't match what I see in the database"

This is a known documentation bug in docs/NOTIFICATION_FRAMEWORK_PHASE441_SERVER_CHECKLIST.md, corrected in this guide — use the actual enum values from §12.1 (Succeeded=2, Failed=5, Retrying=4, Expired=8), not the older checklist's 1/2/3/4 mapping.

18.7 Debug checklist template

[ ] Confirm NotificationEventConfiguration allows the channel for this EventKey
[ ] Confirm a matching AppNotificationTemplate exists (correct EventKey + Channel + Language, or a null-language fallback)
[ ] Confirm a NotificationDeliveryAttempt row exists for this dispatch
[ ] Read ErrorCode/ErrorMessage on any failed attempt
[ ] For Push: confirm an active DeviceToken exists for the recipient
[ ] For SignalR: confirm the recipient was connected at dispatch time; check GetInbox as the durable fallback
[ ] For WhatsApp: walk the 5-step guard chain in §7.3 against the application logs
[ ] Check NotificationRetryQueue / NotificationDeadLetter for the same CorrelationId/EventKey
[ ] Check Hangfire dashboard for the relevant pipeline job's last successful run

18.8 Using SendTestEmail safely

NotificationHistoryController.SendTestEmail (§9.12) has no permission gate — restrict its use to lower environments (local/staging) with a test mailbox, never call it against a production recipient you don't control, and treat any production usage of it as a finding to raise with the platform team, not a supported workflow.

18.9 Logs to check

  • Application logs: search for the EventKey and/or CorrelationId associated with the notification in question — every guard/failure log line in the dispatch and channel-provider code includes at least one of these.
  • Hangfire dashboard: confirm the relevant pipeline job (notification-retry-processor-pipeline, notification-deadletter-cleanup-pipeline, campaign-*-pipeline) is listed as a recurring job and has recent successful executions, not just registered.
  • NotificationDeliveryReceipt table: for WhatsApp specifically, cross-reference ProviderMessageId against what your Meta Business dashboard reports for the same message.