Skip to content

fix: clear stale persisted state when App ID changes during migration#1424

Merged
sherwinski merged 9 commits intomainfrom
fix/resubscribe-on-app-migration
Feb 27, 2026
Merged

fix: clear stale persisted state when App ID changes during migration#1424
sherwinski merged 9 commits intomainfrom
fix/resubscribe-on-app-migration

Conversation

@sherwinski
Copy link
Contributor

@sherwinski sherwinski commented Feb 25, 2026

Description

1 Line Summary

Provides the handleAutoResubscribe() flow with clean state so that stale subscription data does not get used during migration.

Details

When migrating App IDs on the same origin, IndexedDB retains subscription models, push tokens, and lastKnown values from the previous App ID. This causes the SDK to attempt updating a subscription ID that doesn't exist under the new App ID, and prevents checkAndTriggerSubscriptionChanged() from detecting a state change, leaving the subscription stuck as "unsubscribed".

This PR detects the App ID change in initSaveState() and clears all stale persisted state so the auto-resubscribe flow creates a fresh subscription with notification_types = Subscribed.

Systems Affected

  • WebSDK
  • Backend
  • Dashboard

Validation

Tests

Info

Checklist

  • All the automated tests pass or I explained why that is not possible
  • I have personally tested this on my machine or explained why that is not possible
  • I have included test coverage for these changes or explained why they are not needed

Programming Checklist
Interfaces:

  • Don't use default export
  • New interfaces are in model files

Functions:

  • Don't use default export
  • All function signatures have return types
  • Helpers should not access any data but rather be given the data to operate on.

Typescript:

  • No Typescript warnings
  • Avoid silencing null/undefined warnings with the exclamation point

Other:

  • Iteration: refrain from using elem of array syntax. Prefer forEach or use map
  • Avoid using global OneSignal accessor for context if possible. Instead, we can pass it to function/constructor so that we don't call OneSignal.context

Screenshots

Info

Checklist

  • I have included screenshots/recordings of the intended results or explained why they are not needed

Related Tickets



This change is Reviewable

@sherwinski sherwinski force-pushed the fix/resubscribe-on-app-migration branch 3 times, most recently from e5a244b to bfa762e Compare February 25, 2026 18:17
@fadi-george
Copy link
Contributor

If i keep opted in it seems to work fine for migrate.
If I opt out then migrate, I get errors in console and subscription is never created for the second app id.

@fadi-george
Copy link
Contributor

Error seemed to happen here:

  async _handleOnBlur(e: Event): Promise<void> {
    await LoginManager._switchingUsersPromise;

    Log._debug('handleOnBlur', e);
    if (!User._singletonInstance?.onesignalId) {
      return;
    }

    try {
      /**
       * Firefox has 2 focus events with different targets (document and window).
       * While Chrome only has one on window.
       * Target check is important to avoid double-firing of the event.
       */
      if (e.target !== window) {
        return;
      }

      const { onesignalId, subscriptionId } =
        await this._getOneSignalAndSubscriptionIds();
      console.log('zzzonesignalId', onesignalId);
      console.log('zzzsubscriptionId', subscriptionId);
      await this._notifySWToDeactivateSession(
        onesignalId,
        subscriptionId,
        SessionOrigin._Blur,
      );
    } catch (e) {
      Log._error('Error handling blur:', e);
    }
  }

@sherwinski
Copy link
Contributor Author

If i keep opted in it seems to work fine for migrate. If I opt out then migrate, I get errors in console and subscription is never created for the second app id.

Good catch. 5c593b3 makes it so that no error is thrown and no subscription is created on the second app.

@fadi-george
Copy link
Contributor

Rebase

When migrating App IDs on the same origin, IndexedDB retains
subscription models, push tokens, and lastKnown values from the
previous App ID. This causes the SDK to attempt updating a subscription
ID that doesn't exist under the new App ID, and prevents
`checkAndTriggerSubscriptionChanged()` from detecting a state change,
leaving the subscription stuck as "unsubscribed".

This commit detects the App ID change in `initSaveState()` and clears all stale
persisted state so the auto-resubscribe flow creates a fresh subscription with `notification_types = Subscribed`.
When the subscription was opted out during migration, clearing the model store without also clearing `deviceId` causes `isAlreadyRegisteredWithOneSignal()` to return true. This caused session management to call `getOneSignalAndSubscriptionIds()` with no subscription model, throwing a "No subscription" error.

Clearing `userId` alongside the other stale Ids ensures the SDK doesn't enter existing-user session paths when no subscription model exists.
@sherwinski sherwinski force-pushed the fix/resubscribe-on-app-migration branch from 9185076 to 5ba1d63 Compare February 27, 2026 01:43
@sherwinski sherwinski merged commit a85f508 into main Feb 27, 2026
4 checks passed
@sherwinski sherwinski deleted the fix/resubscribe-on-app-migration branch February 27, 2026 01:48
@github-actions github-actions bot mentioned this pull request Feb 27, 2026
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.

2 participants