Skip to content

fix(notification): cancel() crashes on Android with empty notifications list (fix #2341)#3430

Open
avebeatrix wants to merge 1 commit into
tauri-apps:v2from
avebeatrix:fix/notification-cancel-all-android
Open

fix(notification): cancel() crashes on Android with empty notifications list (fix #2341)#3430
avebeatrix wants to merge 1 commit into
tauri-apps:v2from
avebeatrix:fix/notification-cancel-all-android

Conversation

@avebeatrix
Copy link
Copy Markdown

Fixes #2341 (the cancelAll() part — other items in that issue are unaddressed by this PR).

Problem

NotificationPlugin.kt:42 declares the cancel-command argument shape as:

@InvokeArg
class CancelArgs {
  lateinit var notifications: List<Int>
}

The handler then unconditionally reads args.notifications and forwards it to manager.cancel(...). Both supported callers of "cancel all" hit this dead-end:

  • JS cancelAll() from @tauri-apps/plugin-notification invokes plugin:notification|cancel with no notifications field.
  • Rust SDK NotificationExt::cancel_all() (mobile.rs) does run_mobile_plugin("cancel", ()) — same effect.

In both cases Kotlin throws UninitializedPropertyAccessException when the handler reads args.notifications. On the JS side this bubbles up as a rejected invoke() promise that the sendNotification polyfill silently drops, so callers see no notifications cancelled and no error.

This was observed downstream while shipping a reminder-style notification feature — debugging required tracing through the Rust SDK, the plugin's Android Kotlin, and finally dumpsys alarm to confirm that the schedule loop was being aborted by the failing cancelAll() before any notify call ever ran.

Fix

Three small changes, all in plugins/notification/android/src/main/java/:

  1. NotificationPlugin.ktCancelArgs.notifications is now var notifications: List<Int> = listOf() (optional, defaults to empty). The handler routes an empty list through a new manager.cancelAll(); non-empty still goes through the existing manager.cancel(ids).

  2. TauriNotificationManager.kt — new cancelAll() method that enumerates saved IDs via the existing NotificationStorage.getSavedNotificationIds() and reuses the existing per-id cancel() cleanup path (dismiss visible + cancel timer + delete storage entry). No duplicated cleanup logic.

  3. .changes/notification-cancel-all-android.md — patch bump on both notification and notification-js per the repo .changes/readme.md rule («both packages need to be bumped with the same increment even when only one side changed»).

Tests

The plugin's plugins/notification/android/src/test/ currently contains only the placeholder ExampleUnitTest.kt — there is no existing scaffold for handler/manager unit tests. I deliberately did not add a one-off test in this PR so the change stays minimal; happy to add a focused test for cancel(empty) if maintainers want the coverage and can confirm a preferred mocking approach for Invoke / manager.

Compatibility

  • JS cancelAll(): starts working as documented.
  • JS cancel([1, 2, 3]): unchanged — still routed through manager.cancel(ids).
  • Rust NotificationExt::cancel_all(): starts working.
  • Rust NotificationExt::cancel(vec![1, 2, 3]): unchanged.
  • iOS / desktop: untouched (Android-only change).

No public API change. No migration needed.

…ns list (fix tauri-apps#2341)

The `cancel` Android command parses `CancelArgs.notifications` as a
`lateinit var List<Int>`, but the JS `cancelAll()` API and the Rust SDK's
`NotificationExt::cancel_all()` both dispatch the `cancel` command with no
arguments. The handler then throws `UninitializedPropertyAccessException`
when it reads `args.notifications`, which on the JS side surfaces as a
rejected `invoke()` promise that the polyfill silently swallows — so
callers see no notifications cancelled and no error.

Make `notifications` an optional list defaulting to `[]`, and route an
empty list through a new `TauriNotificationManager.cancelAll()` that
enumerates saved IDs from `NotificationStorage.getSavedNotificationIds()`
and reuses the existing `cancel()` cleanup path (dismiss visible +
cancel timer + delete storage entry).

Reported in tauri-apps#2341.
@avebeatrix avebeatrix requested a review from a team as a code owner May 23, 2026 22:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[notification] Absolutely broken plugin in V2

1 participant