Skip to content

πŸ› Bug Report: Inbox badge count drifts after mark-as-read β€” useCounts & useNotifications filter caches go staleΒ #10817

Description

@IPJT

πŸ“œ 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:

  1. 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).
  2. 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:

  1. Ensure the subscriber has at least 2 unread notifications.
  2. In the rendered <Notifications>, click "mark as read" on one notification.
  3. Observe the <Badge> number. ~50% of the time it stays at the pre-click value.
  4. 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?

  • I checked and didn't find a similar issue

🏒 Have you read the Contributing Guidelines?

Are you willing to submit PR?

None

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingtriage

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions