π Description
The unread-count badge for the Inbox drifts out of sync with the actual unread state after mark-as-read operations. Reproducible
~50% of the time in production
Affects both:
- Novu's built-in "mark as read" / "mark all as read" actions rendered by
<Notifications /> inside <Inbox>.
- Custom
notification.read() invoked from an onPrimaryActionClick handler.
A full page reload resyncs the badge.
The underlying cause looks like a combination of two issues in NotificationsCache:
updateNotification mutates the notification in place across every cache-key bucket but never evicts a notification from
buckets whose filter it no longer matches (e.g. a { read: false } bucket after a notification transitions to isRead: true).
getAggregated(filter) concatenates entries from all cache keys whose filter matches by isSameFilter β which ignores limit
β so multiple subscribers of the same filter with different limits yield duplicate notification entries in the aggregated list.
Together these make both useCounts({ filters: [{ read: false }] }) and useNotifications({ archived: false, snoozed: false })
exhibit the same drift.
π Reproduction steps
Minimal repro:
'use client';
import { Inbox, Notifications } from '@novu/nextjs';
import { useCounts } from '@novu/react';
function Badge() {
const { counts } = useCounts({ filters: [{ read: false }] });
return <span>{counts?.[0]?.count ?? 0}</span>;
}
export function Demo() {
return (
<Inbox
applicationIdentifier="β¦"
subscriberId="β¦"
backendUrl="https://eu.api.novu.co"
socketUrl="wss://eu.ws.novu.co"
>
<Badge />
<Notifications />
</Inbox>
);
}
Steps:
- Ensure the subscriber has at least 2 unread notifications.
- In the rendered
<Notifications>, click "mark as read" on one notification.
- Observe the
<Badge> number. ~50% of the time it stays at the pre-click value.
- Reload β badge corrects to the true count.
π Expected behavior
After a mark-as-read (either built-in or via notification.read()), the unread count exposed by useCounts / useNotifications
should converge to the true server state within the same render cycle, with no reload required.
π Actual Behavior with Screenshots
Badge stays stuck on the pre-mutation value roughly 50% of the time in normal usage, until the page is hard-reloaded. In tight
toggle cycles (mark-as-read β mark-as-unread repeatedly) we see ~40% mismatch between the badge and the actual unread notification
dots rendered by <Notifications>.
Novu version
Novu SaaS
npm version
11.11.0 (project uses pnpm 9.10.0)
node version
v24.14.1
π Provide any additional context for the Bug.
No response
π Have you spent some time to check if this bug has been raised before?
π’ Have you read the Contributing Guidelines?
Are you willing to submit PR?
None
π Description
The unread-count badge for the Inbox drifts out of sync with the actual unread state after mark-as-read operations. Reproducible
~50% of the time in production
Affects both:
<Notifications />inside<Inbox>.notification.read()invoked from anonPrimaryActionClickhandler.A full page reload resyncs the badge.
The underlying cause looks like a combination of two issues in
NotificationsCache:updateNotificationmutates the notification in place across every cache-key bucket but never evicts a notification frombuckets whose filter it no longer matches (e.g. a
{ read: false }bucket after a notification transitions toisRead: true).getAggregated(filter)concatenates entries from all cache keys whose filter matches byisSameFilterβ which ignoreslimitβ so multiple subscribers of the same filter with different limits yield duplicate notification entries in the aggregated list.
Together these make both
useCounts({ filters: [{ read: false }] })anduseNotifications({ archived: false, snoozed: false })exhibit the same drift.
π Reproduction steps
Minimal repro:
Steps:
<Notifications>, click "mark as read" on one notification.<Badge>number. ~50% of the time it stays at the pre-click value.π Expected behavior
After a mark-as-read (either built-in or via
notification.read()), the unread count exposed byuseCounts/useNotificationsshould converge to the true server state within the same render cycle, with no reload required.
π Actual Behavior with Screenshots
Badge stays stuck on the pre-mutation value roughly 50% of the time in normal usage, until the page is hard-reloaded. In tight
toggle cycles (mark-as-read β mark-as-unread repeatedly) we see ~40% mismatch between the badge and the actual unread notification
dots rendered by
<Notifications>.Novu version
Novu SaaS
npm version
11.11.0 (project uses pnpm 9.10.0)
node version
v24.14.1
π Provide any additional context for the Bug.
No response
π Have you spent some time to check if this bug has been raised before?
π’ Have you read the Contributing Guidelines?
Are you willing to submit PR?
None