AWRA OpsHub Search

The Send That Delivered Nothing

A push notification that never arrived is not one problem. It is five, they need five different people to fix them, and the worst of them used to be recorded as a success.

Devices, Scanning & Hardware Washingtone Aura 10 min read

The complaint arrives in one shape: "I never got the notification." It is one of the least useful sentences in operations software, because every layer between the decision to notify somebody and the buzz in their pocket can swallow a message, and almost all of them fail quietly. The server can be misconfigured. The credentials can be refused. The device can have been wiped six weeks ago. The provider can be having a bad afternoon. And — the case worth the rest of this page — there can be nobody registered to receive it at all, which is not a delivery failure so much as a delivery that was never attempted.

For a long time our push log recorded four of those five honestly and the fifth as a success. Not through a bug in the ordinary sense: the code did exactly what it said. It reported the outcome of the request it made to the provider. When there were no devices to send to, it made no request, encountered no error, and wrote down that the send was fine.

The distinction the whole post turns on

There is a difference between "the API call I made succeeded" and "the notification I was asked to deliver arrived". A system that reports the first while appearing to report the second is not merely incomplete — it is actively reassuring, and it is most reassuring precisely when nothing happened at all.

A send with nothing to send to

Device registration happens when somebody signs in on the mobile app and accepts the notification prompt. So the audience for a push is not the people you selected — it is the subset of them who have signed in on a phone and said yes. Those two sets are very different in the first month of a rollout, and nothing about the old log made that visible.

The consequence is a specific and quite unpleasant failure mode. An administrator configures an alert, tests it against their own account, sees it work, and switches it on for a team of thirty warehouse staff who have never opened the app. Every subsequent send is written down as sent. The log is green. The feature is, from the log's point of view, working perfectly, and it is delivering nothing to anybody.

The same send, under the old reporting and the new

Audience selected 30 warehouse staff
Devices actually registered 0 — nobody has signed in on the app yet
Requests made to the provider None. There was nothing to address
What the log used to say Sent
What the log says now Failed — missing token, severity high
The change A send that reached zero devices is a failure, including when the provider was never called

The rule is now explicit: a send with no device tokens is a failure unless it was addressed to a topic rather than to devices, because a topic send legitimately has no token list of its own. That exception is the only one, and it exists because without it every topic broadcast would be reported as broken.

This also means the "failed only" filter includes sends that were never attempted. That is deliberate and it is the point. The filter answers "which of my notifications did not arrive", not "which of my API calls returned an error".

Five causes, five different people

Once a failure is recorded, "failed" is still not an answer anybody can act on. So every failure is classified into one of five categories, and the categories are chosen by who has to do something about it rather than by what the provider's error string happened to say.

  • Missing token — nobody registered. Not a technical fault at all. The fix is a person signing in on the app, which means this is a rollout problem wearing an engineering costume.
  • Configuration — the server's credentials are absent or unreadable. Nothing will ever be delivered until somebody with server access fixes it. Highest severity, because it is total.
  • Permission denied — the credentials exist and the provider refused them, or the device belongs to a different sender. Someone has to change a role or reconcile two projects.
  • Stale token — the app was uninstalled, its storage cleared, or the token rotated. Normal attrition. The devices are purged and re-register on next sign-in.
  • Provider error — the request was accepted and could not be delivered. An outage, a quota, a transient fault. Retry, then go and look at the provider's own status page.

Two things about that list are worth more than the list itself. The first is the order. When one send trips several failures at once — twenty devices, some stale, some rejected, some fine — the row is labelled with whichever category blocks the most delivery, not the most common one or the first one encountered. A configuration fault affecting everything outranks a handful of dead handsets, because fixing the dead handsets while the credentials are broken achieves nothing.

The second is that configuration is tested before anything else, and for a reason that took a while to see: a missing service account does not announce itself as a configuration problem. It surfaces as a failure to mint an access token, which reads exactly like a credentials rejection. Classify on the error's shape and you send somebody to audit permissions in a provider console for a file that is simply not on the server.

The same three words, two different remedies

Here is the detail that convinced me this classification was worth building properly rather than mapping error codes to labels and moving on.

The provider has two generations of messaging API, and both have an error meaning "this token belongs to a different sender". On the legacy path it appears as one spelling; on the current path, as another. Same words, same underlying situation, and the correct response is not the same.

One condition, two codes, two categories

Legacy path — MismatchSenderId Classified as a stale token
Current path — SENDER_ID_MISMATCH Classified as permission denied
Why they differ On the legacy path this only ever means the token is from another project
What that makes the fix Drop the token and let the device re-register — identical to any stale token
What the other one needs A human reconciling two provider projects, or granting a role
The general rule Categorise by the remedy, not by the message. The message is the provider's vocabulary; the remedy is yours

A diagnostics screen that grouped both under one heading would be defensible, tidy, and would send half its readers to do the wrong job. The categories exist to shorten the distance between a complaint and the person who can end it, and that means the taxonomy has to be organised around the fix.

Per-device, not per-send

One send goes to many devices and the results are mixed far more often than not. So the error is now stored against each individual token rather than collapsed into a single verdict for the send, and the failing tokens are shown with the reason each one gave. That is what makes the difference between "this send partly failed" and "these four handsets are dead, here they are, purge them" — and the second is the only version anybody can act on.

Stale tokens accumulate quietly and forever otherwise. Every uninstall leaves one, and each one produces a failure on every subsequent send, which slowly poisons any figure you might compute about delivery. Purging them is a listed action rather than a background job on purpose: the devices come back on their own at the next sign-in, and an organization should be able to see how much of its audience it just lost.

The window is thirty days, and that is a choice with a cost

Diagnostics cover the last thirty days. Long enough that a pattern is visible, short enough that a fault fixed six weeks ago has stopped colouring the summary. The cost is that a slow seasonal problem — a device fleet that goes quiet every quarter — is harder to see here than in a longer view. Worth knowing before you use this screen to answer a question about last year.

The question to ask anybody, including us

Notification delivery is one of the easiest things in operations software to demonstrate and one of the hardest to be honest about, because the demonstration is always performed on a device that is signed in, registered, and sitting on the desk.

  • What does your log say when a notification is sent to somebody with no registered device? If the answer is "sent", or if nobody knows, that is the finding. It was our answer too.
  • Can I see which specific devices failed, and why each one failed? A per-send verdict is not diagnosable. Ask to see a failure list with reasons attached to individual devices.
  • Does the failure tell me who has to fix it? "Delivery error" names a symptom. Ask whether the system distinguishes a server misconfiguration from an uninstalled app, because those go to different people on different days.
  • Are dead device registrations ever cleaned up, and do I get to see how many there were? Silent cleanup hides the size of an audience you have lost.

Our own answers: failed with a high-severity missing-token reason since August 2026 and "sent" before that; yes, per token with its own error code; yes, five categories chosen by remedy and ordered by how much delivery they block; and yes, purging is something you do deliberately and can count. The first of those is the one we would rather not have had to write down, which is roughly the test for whether a post like this is worth publishing.

Two adjacent failures are worth reading alongside this one, because together they make a pattern rather than three incidents. The Alarm That Needed A Login is the same class of mistake one layer up — an alert that was correctly generated and sent to an audience that structurally could not receive it. The Promise Nothing Was Watching is the version where the notification was never owed at all, because nothing was monitoring the commitment it would have been about. A missing token, a missing audience, a missing watcher: all three present to a user as silence, and only one of them is a delivery problem.

Help Center

Need a quick answer while you read?

Run inventory, procurement, assets, sales, and field work with approved AWRA guidance for setup, migration, integrations, security, pricing, and support.

Search all approved AWRA public help articles.

Open Help Center