Repository navigation
[Bug]: Live Activity update token is never re-uploaded after a failed first attempt until the next app launch (no foreground re-drive, same-token enter dropped) #1769
Description
What happened?
A Live Activity started via push-to-start stays on its initial content state for the rest of the session: every update notification the server sends for that activity_id reports "No recipients", because the per-activity update token was never bound server-side. Opening the app does not repair it. The activity only recovers after the app is cold-started (or after the push subscription id changes).
Reading the Live Activities module (byte-identical in 5.5.5, 5.7.0 and 5.8.0, verified with diff -r between the tags), the per-activity update token is uploaded exactly once and nothing re-drives a failed upload while the app keeps running:
- The upload is a single attempt with no background execution.
OneSignalLiveActivitiesManagerImpl.listenForActivityPushToUpdatecallsenteras soon as ActivityKit yields the token (OneSignalLiveActivitiesManagerImpl.swift:313-328);OSLiveActivitiesExecutor.appendexecutes it immediately (OSLiveActivitiesExecutor.swift:175-187) over a defaultNSURLSession(OneSignalClient.m:53-70). NobeginBackgroundTaskis held anywhere in the Live Activities path, although push-to-start delivers the token during a short background wake, so the request is routinely torn by suspension. Other SDK paths (OSOperationRepo, the user executors) do useOSBackgroundTaskManager; this one does not. - A retryable failure is retained but never retried in-process.
executeRequest's failure block keeps the request for any status other than 400/401/402/403/404/409/410 and the comment says it "will retry the next time the app starts" (OSLiveActivitiesExecutor.swift:241-253,OSNetworkingUtils.m:41-53).OneSignalClientre-attempts status 0 / 5xx up to four times at 5/15/45/135 s, but on in-memoryperformSelector:afterDelay:main-queue timers (OneSignalClient.m:150-176) that only advance while the process runs; once they are exhausted, or on a code it never re-attempts (429 from the per-app rate limit, or a 5xx/0 with a non-JSON body, which fails before the re-attempt check atOneSignalClient.m:204-220), the entry is dead until the next launch. - The only re-drives are the one-shot launch poll and a subscription-id change.
pollPendingRequestsis a singleasyncAfter(30 s)(OSLiveActivitiesExecutor.swift:189-202) armed fromstart()(:152-158), reached only from+init(OneSignal.m,startLiveActivitiesManager), and fromonPushSubscriptionDidChange(:160-173). There is no foreground, new-session, reachability or periodic flush;OneSignal.mstill carries// [self executePendingLiveActivityUpdates];commented out in the on-session path. - A repeated
enterwith the same token is dropped, whether the cached entry is pending or successful (OSRequestSetUpdateToken.swift:65-75returnsfalsefor an identical token;appendlogs "superseded request not saved/executed"). So an app cannot work around the gap by callingenteragain on foreground. onPushSubscriptionDidChangere-marks only start tokens. Thecaches { _ in self.startTokens.markAllUnsuccessful() }closure ignores its argument (OSLiveActivitiesExecutor.swift:166-171), so a successful update-token binding is never re-sent under the new subscription id; combined with (4) that binding is sticky until the 24 h cache TTL or a token rotation.
Environment: React Native app via react-native-onesignal 5.5.14 (CocoaPods, OneSignalXCFramework 5.7.0; the affected production build ran 5.5.5), Objective-C++ app delegate, setupDefault() called natively from didFinishLaunchingWithOptions (the wrapper initializes the SDK natively before the delegate body, so OneSignal.initialize has run). Activities are started and updated server-side through the REST API (activity_id addressing).
Steps to reproduce?
Deterministic from source, using Airplane Mode to make the single upload fail:
- Debug build with
OneSignal.Debug.setLogLevel(.LL_VERBOSE); iOS 18+ device;OneSignal.LiveActivities.setupDefault()in use. - Start a Live Activity (push-to-start from the server, or
startDefault) and switch Airplane Mode on right after it appears, before the token upload completes. Log:OneSignal.LiveActivities executing request: (OSRequestSetUpdateToken) ...followed byrequest failed with error ... code 0, then the four re-attempts over ~200 s while the app stays in the foreground. - After the re-attempts are exhausted, switch Airplane Mode off and background/foreground the app several times.
- Send an update to the activity from the dashboard or REST API: it reports No recipients. The log shows no new
executing requeston any foreground; a secondenterfor the same token logssuperseded request not saved/executed. - Kill and relaunch the app: ~30 s later
executing outstanding requestsruns, the token is bound, and the next update is delivered.
The same end state is reached whenever the first upload does not land during the push-to-start background wake (the process is suspended mid-request) or the endpoint answers 429.
What did you expect to happen?
- A failed or unprepared update-token upload is re-driven while the app is running: at least on foreground / new session (the commented-out
executePendingLiveActivityUpdateshook), and ideally with the poll re-armed after a retryable failure instead of waiting for the next launch. - The upload made during the push-to-start wake is protected by a bounded
UIApplicationbackground task, like the SDK's other executors. - 429 is honoured (Retry-After or backoff) rather than retained without any re-attempt.
onPushSubscriptionDidChangere-sends update tokens too, so a binding made under a previous subscription id is refreshed.
Workaround we ship meanwhile (public API only): on didBecomeActive, when an activity is running, call enter(activityId, withToken:) for each Activity<DefaultLiveActivityAttributes> (covers the 4xx-evicted case) and then OneSignalLiveActivitiesManagerImpl.start(), which re-arms the 30 s poll over every unsuccessful cached request, plus a background task around the push-to-start wake. It cannot address point 5.
OneSignal iOS SDK version
5.5.5 (affected production build), 5.7.0 (current), module unchanged in 5.8.0 (f5e24a8a and 4f9dc75f compared)
iOS version
iOS 18, iOS 26
Specific iOS version
iOS 26.x (iPhone 17 Pro, production occurrence); the code path is version-independent.
Relevant log output
Server side for the affected activity: 14 consecutive update notifications over 14 minutes with "No recipients", then "Sent (1)" on the first update after a long foreground. App logs for the incident device were not captured; the SDK's own verbose lines that reproduce the state are OneSignal.LiveActivities request failed with error (code 0), Re-scheduling request (OSRequestSetUpdateToken) to be re-attempted in 5.000 seconds x4, then OneSignal.LiveActivities superseded request not saved/executed on any later enter, and nothing until OneSignal.LiveActivities executing outstanding requests after a relaunch.
Code of Conduct
- I agree to follow this project's Code of Conduct
Activity