Skip to content

[Bug][iOS] ResetApplicationIconBadgeNumber: ArgumentNullException when GetDeliveredNotifications returns null #545

Description

@mos379

Description

LocalNotificationCenter.ResetApplicationIconBadgeNumber(UIApplication) crashes with
ArgumentNullException (paramName: "collection") when iOS invokes the
GetDeliveredNotifications completion handler with a null array.

Platforms/iOS/LocalNotificationCenter.cs#L62-L66:

UNUserNotificationCenter.Current.GetDeliveredNotifications((notificationArray) =>
{
    notificationList.AddRange(notificationArray);   // throws if notificationArray is null
    completionSource.SetResult(true);
});

List<T>.AddRange throws ArgumentNullException on a null argument, and the
callback parameter is not guarded.

The surrounding try/catch does not help

ResetApplicationIconBadgeNumber wraps its body in try/catch, but the throw happens
inside the completion handler, which iOS invokes later on its own stack via the
ObjC trampoline. The try block has already returned by then, so the exception is
unhandled and terminates the app rather than being caught.

There is a second consequence: completionSource.SetResult(true) on line 65 never
runs, so completionSource.Task.Wait() on line 67 would block indefinitely if the
process survived.

Stack trace

System.ArgumentNullException: ArgumentNull_Generic Arg_ParamName_Name, collection
   at System.Collections.Generic.List`1[[UserNotifications.UNNotification, Microsoft.iOS,
        Version=26.5.0.0, Culture=neutral, PublicKeyToken=84e04ff9cfb79065]]
        .AddRange(IEnumerable`1 collection)
   at Plugin.LocalNotification.LocalNotificationCenter.<>c__DisplayClass26_1
        .<ResetApplicationIconBadgeNumber>b__0(UNNotification[] notificationArray)
   at ObjCRuntime.Trampolines.SDActionArity1V276.Invoke(IntPtr block, NativeHandle obj)

Surfaced as SIGABRT in crash reporting.

Suggested fix

UNUserNotificationCenter.Current.GetDeliveredNotifications((notificationArray) =>
{
    if (notificationArray is not null)
    {
        notificationList.AddRange(notificationArray);
    }
    completionSource.SetResult(true);
});

Moving SetResult outside the guard also removes the indefinite-wait path.

The async variant ResetApplicationIconBadgeNumberAsync (line 103) reads
notificationList.Length from GetDeliveredNotificationsAsync() without a null check
either, so it may warrant the same treatment.

Environment

Plugin.LocalNotification 14.1.1
Platform iOS (.NET MAUI, net10.0-ios)
Microsoft.iOS 26.5.0.0

Notes

Observed in production crash reporting, low frequency — 3 occurrences on a single
device. I have not reproduced it deliberately; a null array from
GetDeliveredNotifications appears to be an infrequent iOS behaviour rather than
something an app can trigger on demand. Reporting it because the fix is a one-line
guard and the failure is fatal.

Possibly adjacent to #236, which was a different exception in the same
iOS notification-enumeration area.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions