A phone showed a notification for a new message. The conversation was already open, but the message wasn’t there.

We found this while working on a messaging app. The notification and the conversation were updated through separate paths. One had finished its job. The other hadn’t, and we’d left the app waiting for it.

The odd part was how much had gone right. The server had saved the message. The notification had reached the phone. Yet someone looking at the conversation still had nothing to read.

Why the notification arrived without the message

An open conversation normally receives updates through a live connection called a WebSocket. Push notifications take another route, allowing the phone to announce a message even when the app isn’t in use.

When a notification arrived with our app in the background, the app checked for new messages. When it arrived with the app open, we displayed the banner and stopped. We expected the live connection to handle the rest.

If that live update was missed or delayed, the conversation stayed behind. The phone had received a useful clue and we weren’t acting on it.

We changed the open app to run the same check as the background app: ask the server for missing messages and save them on the phone. The conversation could then update from those saved records.

Why not put the notification straight into the conversation?

That’s an appealing shortcut. The notification is here. Use it.

But the screen already reads from the messages stored on the phone. Feeding it directly from notifications would introduce another version of the conversation to keep in agreement. We already had a way to fetch and store the server’s records, so we used it.

Sometimes the live update gets there first. Sometimes the notification does. Both can lead to the same message, which means handling it twice must still leave only one copy.

There was another timing detail to protect: if a notification arrives while the app is already checking for messages, it must be able to request another check. Otherwise, combining overlapping requests can accidentally throw away news of something newer.

Follow the part that worked

“The message is slow” could send you looking almost anywhere.

“The notification is here, but the message isn’t” gives you somewhere specific to start. We knew word of the message had reached the phone. What happened after that?

In this case, one branch of our code stopped too early.

The repair doesn’t guarantee that every notification or live update will arrive. It gives the conversation a way to catch up when a notification does reach it. The phone had already told us where to look.