Reported by @Whatamidoing-WMF
I haven't been able to reproduce this locally, but my theory is that this is a race condition caused by rECHOcd57ab5e5428: Clicking a marked-as-unread notification should mark it as read (https://gerrit.wikimedia.org/r/#/c/277703/). That change made all notifications be marked as read on click, even if there is also an echo_target_page row for them. I think these notifications are being marked as read twice, and subtracted from the notification count twice:
- User clicks unread notification
- JS sends API request to mark notification as read; this decrements the unread messages counter in memcached (from 1 to 0) and deletes the echo_target_page row in the database
- Browser navigates to page
- onPersonalUrls sees an already-decremented message counter (i.e. 0) in memcached, but also still sees the echo_target_page row in the DB (due to slave lag)
- A deferred update is scheduled for mark this notification as read (again) and to account for this, 1 is subtracted from the message count that's sent to the client
- 0 - 1 = -1, so the client receives a message count of -1
