Skip to content

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

@IPJT

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

No one assigned

    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