Repository navigation
[Bug]: Android SDK 5.9.9: invalid create-subscription pauses the operation queue across restarts #2747
Description
What happened?
An operation group login-user + create-subscription was rejected with HTTP 400 ("Subscription can't change notification_types to zero or negative integer while enabled"), the SDK paused its persistent operation repository, and the same invalid group was retried after every restart and after notification permission was granted. The signed-in user never became addressable by external ID. Full sanitized report below.
Steps to reproduce?
See "Application sequence and reproduction steps" below (field reproduction: notifications disabled, app data cleared, restarts, new account, later permission grant).
What did you expect to happen?
Missing permission, a missing remote alias, an initial logout or a later login should not leave a persisted operation group that can never progress; after granting permission the user should be created/associated.
OneSignal Android SDK version
5.9.9 (via react-native-onesignal 5.5.9)
Android version
13
Specific Android models
Samsung SM-T970
Relevant log output
Sanitized timeline included below; sanitized logcat excerpt available on request.
Code of Conduct
- I agree to follow this project's Code of Conduct
Environment
- Catitin, React Native app, package
com.catitin.app. - Samsung SM-T970, Android 13 / API 33, pilot APK build 11.
react-native-onesignal5.5.9; its Android dependency is OneSignal 5.9.9.- Observed on 15 September 2026, local time Europe/Madrid (UTC+02:00).
- The existing pilot APK downloaded a JavaScript update at 01:33:14; it was applied on a subsequent restart. The APK/native SDK did not change.
Observed failure
A user update returned HTTP 404 for a missing onesignal_id alias. A subsequent operation group containing login-user and create-subscription returned HTTP 400:
Subscription can't change notification_types to zero or negative integer while enabled
The SDK logged that it was pausing the operation repository. The same rejection and pause recurred after process death and restart. The new Catitin profile could not subsequently be found by its external ID, according to a separate backend inspection. Granting notification permission later did not restore that profile's association in the observed session.
This is a field reproduction in our application, not yet a standalone SDK reproducer. We have not established which SDK operation originally created the inconsistent subscription state.
Application sequence and reproduction steps
The application version tested behaved as follows:
- Initialize OneSignal at startup without requesting notification permission.
- Start an auth bridge. If restored auth confirms that no session exists, call
OneSignal.logout()once to release any previous account. A temporary missing profile does not cause logout. - Once a signed-in person's application profile loads, call
OneSignal.login(profileId)without first checking notification permission. Language and tags were also written separately. - After new account registration, show an application explanation sheet. Its affirmative action calls
OneSignal.Notifications.requestPermission(false). The notification settings screen can instead call it withtrueto allow the SDK's settings fallback.
The tester performed this sequence on the pilot APK:
- Disable Catitin notifications in Android Settings, then clear Catitin application data.
- Delete and recreate a disposable application account. This deletes our application account; we have not confirmed whether the server-side OneSignal user was deleted. Do not assume a clean OneSignal backend.
- Launch Catitin, allow the pilot JavaScript update to download, remove the task and reopen to apply it. There were further task removals/reopens in this run; see the timestamps below.
- Complete registration. The explanation sheet appears before entering the room.
- Leave the app with the sheet open, then return. The tested application had a separate sheet lifecycle defect; the tester eventually requested notifications from Catitin's notification settings screen.
- Grant notification permission and return to Catitin. Inspect the operation queue and the application's external-ID association.
For an independent SDK reproduction, preserve the initial no-session logout(), the missing-permission startup and the restarts as well as the later profile login(). Testing only login(newProfile) would omit an earlier failure already visible in this trace.
Sanitized timeline
All times below are local on 15 September 2026. Line references point to our internal sanitized logcat excerpt, which we can share on request.
| Time | Observation |
|---|---|
| 01:33:12 | Application process starts after data reset. |
| 01:33:14 | Pilot JavaScript update download completes. |
| 01:33:20 / 01:33:23 | Process killed for remove task, then restarted. |
| 01:33:35.037 | PATCH 404: no user with the requested onesignal_id alias (line 716). |
| 01:33:35.342–343 | POST 400 above; grouped login-user + create-subscription; operation repository paused (717–718). |
| 01:33:44 / 01:33:48 | Another task removal and new process. |
| 01:33:53.912 | Same HTTP 400 and queue pause (737–738). |
| 01:34:19 | New application profile created, according to the backend inspection: 23:34:19 UTC on 14 September. |
| 01:39:06 / 01:39:08 | Process killed for remove task, then restarted. |
| 01:39:08.836 | Same HTTP 400 and queue pause (765–766). A separate session-metric request also rejects an unresolved local ID (767). |
| 01:39:35–38 | OneSignal PermissionsActivity, then Android GrantPermissionsActivity; return to MainActivity. |
| 01:47:20 | A later inspection reported another invalid retry through SyncJobService; this later sample is described in our diagnostic report but is not included in the supplied raw excerpt. |
Limits of the evidence
- The first 400 precedes creation of the new application profile by about 44 seconds. We cannot attribute that first queued failure to
login(newProfileId). - The visible, complete
login-userobject in the first failed group has noexternalIdfield. Thecreate-subscriptionpart showstype:PUSHandenabled:true, but that raw line is truncated. Its address is intentionally omitted here. - A later diagnostic inspection reports
status:NO_PERMISSIONalongsideenabled:true; the initial truncated raw line alone does not establish that status field. The server's 400 does establish that the requested permission/enabled state was invalid. - The later external-ID lookup returning 404 and the 01:47:20 retry are reported by our separate diagnostic inspection, not independently re-queried while preparing this report.
- An earlier 409 in the original trace belongs to another account before the reset. We do not identify it as the cause of this episode.
- No email address, device push token, credential, OneSignal App ID, or real profile/user/subscription UUID is included in this report. Please request a suitably redacted diagnostic export if more queue data is needed.
Expected behavior and questions
A missing notification permission, a missing remote alias, an initial logout, or a later login should not leave a persisted operation group unable to progress after permission is granted. Please help identify the path generating the inconsistent create-subscription and a supported recovery for an already persisted invalid group.
PR #2627 documents a related subscription-state issue and explicitly leaves a sibling LoginUserOperationExecutor.createSubscriptionsFromOperation path outside its change. That makes it relevant to investigate; we have not proven this trace uses the same root cause. PR #2636 excludes NO_PERMISSION from its subscription self-healing. Queue behavior can be examined in OperationRepo at 5.9.9.
Application mitigation and follow-up field test
We deployed an application change (pilot channel, same APK/native SDK 5.9.9) that calls OneSignal.login(profileId) only after getPermissionAsync() returns true (at startup, after requestPermission resolves true, on permissionChange, and when the app returns to foreground), and never calls logout() for a merely missing permission. The confirmed no-session logout() remains.
Follow-up test on the same tablet the next morning, starting again from cleared app data with notifications disabled:
- Two launches without signing in and without permission: no HTTP 400 and no "pausing the operation repo".
- New account, explanation sheet answered "Not now": no
login-userattempt, no errors. - Permission enabled in Android Settings, then returning to the app: the profile became addressable by external ID with one enabled push subscription, and a test notification was delivered.
- Sign-out and sign-in with a different account: the subscription moved to the new user (expected 409 during login), no queue pause.
So deferring login() until permission is granted avoided the failure in our tests. It does not purge an already invalid persisted queue, and we still do not know which operation created the first invalid group in the original trace. We think the SDK should not be able to persist a create-subscription that the backend always rejects, nor keep retrying it after permission is granted.
Activity
nan-li commentedon Sep 28, 2026
Hi @nowisyes thanks for the detailed reporting.
The issue you ran into is corrected in Android SDK
5.10.1by #2728 (react-native-onesignalversion5.5.12and later). Using that release prevents new occurrences. Separately, we are working on fixes to recover those devices that run into this issue and get stuck.On your mitigation: deferring
login()until permission is granted avoids the trigger, but it also means users who decline notifications are not identified by external ID, which affects the user's data syncing. Once you are onreact-native-onesignalversion 5.5.12 or later you should be able to calllogin()as soon as the profile loads again. Callinglogout()with no user logged in is a no-op and did not contribute.antoinedelp commentedon Oct 27, 2026
Hi @nan-li, thanks for the follow-up on this issue. We are hitting the same failure in production and would like to know where the recovery fix stands.
Environment
react-native-onesignal5.5.14, so OneSignal Android SDK 5.10.2 (iOS SDK 5.7.0 works fine)login()is called once notification permission is granted, the same sequence as the original reportWhat we observe
Over the last 6 days, 407 Android users (4,461 occurrences) end up in this state, 15 seconds after
login():The same users report it again on every app launch, so the state persists across restarts. This matches the paused, persisted operation queue described above.
On a later launch the SDK already holds our external id.
LoginHelperthen treatslogin()with the same id as a no-op, so nothing we call from JS gets the user created again.Questions
react-native-onesignal?logout()followed bylogin()reach the backend while the queue is paused, or does the invalid group stay at the head?login()as soon as the profile loads on 5.5.12+. Is that enough to prevent new occurrences on 5.10.2? Fresh installs on 5.10.2 are affected. Among accounts created since Oct 22 (all installed with SDK 5.10.2, never on an older version), 23 of 120 Android users who granted push permission (19%) end up stuck in the state above, versus 1 of 119 on iOS. So 5.10.1+ does not prevent new occurrences for us.We can share sanitized diagnostics and the affected external ids privately with support if that helps.
Thanks!