fix: [SDK-4792] keep devices subscribed when a push token fetch temporarily fails#2670
Merged
Conversation
…h errors A tokenless, transient FCM/HMS token-fetch error (e.g. -9, -11) could overwrite an already-SUBSCRIBED push subscription and flip it to unsubscribed on the backend. The prior guard lived in PushTokenManager using in-memory state that reset on process restart, so a cold start during an FCM/HMS outage could still persist the error. - Add SubscriptionStatus.isRetryableTokenError covering the transient FCM/HMS codes (-8, -9, -11, -12, -25, -27, -29). - Add a durable guard in SubscriptionManager.addOrUpdatePushSubscriptionToken that ignores a tokenless retryable error when the persisted subscription is already SUBSCRIBED with a valid token. Real opt-outs / permission changes are not retryable and still downgrade as before. - Add tests for the guard and the new flag; correct an existing test that paired a non-null token with an error status (an impossible production combination). Co-authored-by: Cursor <cursoragent@cursor.com>
Contributor
📊 Diff Coverage ReportDiff Coverage Report (Changed Lines Only)Gate: aggregate coverage on changed executable lines must be ≥ 80% (JaCoCo line data for lines touched in the diff). Changed Files Coverage
Overall (aggregate gate)17/17 touched executable lines covered (100.0% — requires ≥ 80%) |
abdulraqeeb33
approved these changes
Jun 25, 2026
…iption update Check the guard's side effect, not just the final model values: a retryable token-fetch error with no token emits no model change (so no subscription update is sent), while a non-retryable status still emits one. Also correct the -29 (AUTHENTICATION_FAILED) doc to note it is retryable, matching how PushRegistrator already classifies it. Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
One Line Summary
A temporary push token retrieval error no longer marks an already-subscribed device as unsubscribed.
Details
Motivation
Some devices showed as Unsubscribed in OneSignal even though they could still receive push.
When the SDK briefly can't get a push token from FCM/HMS (for example a short network or Google/Huawei service hiccup), it produces an error code such as -9 or -11 with no token in the response. The SDK saved that error onto a subscription that was already subscribed, which told the OneSignal backend the device was unsubscribed.
There was already a check meant to prevent this, but it relied on a value kept only in memory. That value is lost when the app process restarts, so opening the app fresh during an outage could still trigger the bug.
Scope
SubscriptionManager, the single place push subscription status is saved. It ignores a temporary, no-token error when the subscription is already subscribed and still has a token. Because it reads the saved subscription, it also holds after an app restart.Other
The error codes treated as temporary (ignored only when a valid token already exists): -8, -9, -11, -12 (FCM), -25, -27 (HMS), and -29 (FCM auth). They're grouped behind a new internal
SubscriptionStatus.isRetryableTokenErrorflag.Testing
Unit testing
Added to
SubscriptionManagerTests:Manual testing
On a device already subscribed with a valid token, forced push registration to return -9 (SERVICE_NOT_AVAILABLE) with no token, then cold-started the app:
Affected code checklist
Checklist
Overview
Testing
Final pass
Made with Cursor