Skip to content

App Crash with error message "There is no singleton instance. Make sure you configure Purchases before trying to get the default instance" #360

@hellosagar

Description

@hellosagar

Describe the bug
Observed a rare crash in production that appears to be related to the initialization and usage of the Purchases.sharedInstance. This crash does not happen consistently—it occurs only occasionally and not during every user session.

Context:

  • The Purchases SDK is initialized at app startup in the app’s main initialization flow.
  • The only other place where Purchases.sharedInstance is accessed is the Paywall screen, which is only presented when the user manually triggers the premium flow (typically at least 5+ seconds after app launch).
  • Despite this delay, the crash is being reported from the Paywall screen, suggesting a potential issue with either the timing of SDK initialization or how/when Purchases.sharedInstance is being accessed.

Here is the app init code

LaunchedEffect(Unit) {
    Purchases.logLevel = LogLevel.INFO
    Purchases.configure(apiKey = billing.apiKey()) {
    }
    Purchases.sharedInstance.getCustomerInfo(
      onSuccess = { customerInfo ->
            // do somehting
      },
      onError = {

      }
    )
  }

and here is the code from the paywall screen

val options = remember {
    PaywallOptions(
      dismissRequest = {
        handleNavigation(false, coroutineScope, source, navController, localConfigRepository)
      }
    ) {
      shouldDisplayDismissButton = true
      listener = object : PaywallListener {
        override fun onPurchaseCancelled() {
          super.onPurchaseCancelled()
          println("-_- RC Purchase cancelled")
        }

        override fun onPurchaseCompleted(
          customerInfo: CustomerInfo,
          storeTransaction: StoreTransaction
        ) {
          super.onPurchaseCompleted(customerInfo, storeTransaction)
          println("-_- RC Purchase completed")
        }

        override fun onPurchaseError(error: PurchasesError) {
          super.onPurchaseError(error)
          println("-_- RC Purchase error")
        }

        override fun onPurchaseStarted(rcPackage: Package) {
          super.onPurchaseStarted(rcPackage)
          println("-_- RC Purchase started")
        }

        override fun onRestoreCompleted(customerInfo: CustomerInfo) {
          super.onRestoreCompleted(customerInfo)
          println("-_- RC Restore completed")
        }

        override fun onRestoreError(error: PurchasesError) {
          super.onRestoreError(error)
          println("-_- RC Restore error")
        }

        override fun onRestoreStarted() {
          super.onRestoreStarted()
          println("-_- RC Restore started")
        }
      }

    }
  }

  Paywall(
    options = options
  )

This issue has 2 crash events affecting 1 user

Image

Stacktrace

Fatal Exception: A8.H: There is no singleton instance. Make sure you configure Purchases before trying to get the default instance. More info here: https://errors.rev.cat/configuring-sdk
at com.revenuecat.purchases.Purchases$Companion.getSharedInstance(Purchases.kt:11)
at com.revenuecat.purchases.ui.revenuecatui.data.PurchasesImpl.<init>(PurchasesType.kt:3)
at com.revenuecat.purchases.ui.revenuecatui.data.PaywallViewModelImpl.<init>(PaywallViewModel.kt:2)
at com.revenuecat.purchases.ui.revenuecatui.data.PaywallViewModelFactory.create(PaywallViewModelFactory.kt:23)
at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.android.kt:3)
at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.android.kt:1)
at androidx.lifecycle.viewmodel.ViewModelProviderImpl_androidKt.createViewModel(ViewModelProviderImpl.android.kt:16)
at androidx.lifecycle.viewmodel.ViewModelProviderImpl.getViewModel$lifecycle_viewmodel_release(ViewModelProviderImpl.kt:57)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.android.kt:13)
at androidx.lifecycle.viewmodel.compose.ViewModelKt__ViewModelKt.get(ViewModel.kt:63)
at androidx.lifecycle.viewmodel.compose.ViewModelKt.get(ViewModel.kt:1)
at androidx.lifecycle.viewmodel.compose.ViewModelKt__ViewModelKt.viewModel(ViewModel.kt:79)
at androidx.lifecycle.viewmodel.compose.ViewModelKt.viewModel(ViewModel.kt:1)
at com.revenuecat.purchases.ui.revenuecatui.InternalPaywallKt.getPaywallViewModel(InternalPaywall.kt:133)
at com.revenuecat.purchases.ui.revenuecatui.InternalPaywallKt.InternalPaywall(InternalPaywall.kt:123)
at com.revenuecat.purchases.ui.revenuecatui.PaywallKt.Paywall(Paywall.kt:60)
at com.revenuecat.purchases.kmp.ui.revenuecatui.PaywallKt.Paywall(Paywall.kt:31)
at com.sample.packagename.paywall.PaywallScreenRouteKt.PaywallScreenRoute(PaywallScreenRoute.kt:225)
at com.sample.packagename.MainScreenKt$MainScreenRoute$4$1$3$1$1$3.invoke(MainScreen.kt:119)
at com.sample.packagename.MainScreenKt$MainScreenRoute$4$1$3$1$1$3.invoke(MainScreen.kt:13)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:45)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:3)
at androidx.navigation.compose.NavHostKt$NavHost$32$1.invoke(NavHost.kt:2)
at androidx.navigation.compose.NavHostKt$NavHost$32$1.invoke(NavHost.kt:1)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:45)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:1)
at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:31)
at androidx.compose.runtime.saveable.SaveableStateHolderImpl.SaveableStateProvider(SaveableStateHolder.kt:175)
at androidx.navigation.compose.NavBackStackEntryProviderKt.SaveableStateProvider(NavBackStackEntryProvider.kt:140)
at androidx.navigation.compose.NavBackStackEntryProviderKt.access$SaveableStateProvider(NavBackStackEntryProvider.kt:1)
at androidx.navigation.compose.NavBackStackEntryProviderKt$LocalOwnersProvider$1.invoke(NavBackStackEntryProvider.kt:2)
at androidx.navigation.compose.NavBackStackEntryProviderKt$LocalOwnersProvider$1.invoke(NavBackStackEntryProvider.kt:1)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:45)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:1)
at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:31)
at androidx.navigation.compose.NavBackStackEntryProviderKt.LocalOwnersProvider(NavBackStackEntryProvider.kt:130)
at androidx.navigation.compose.NavHostKt$NavHost$32.invoke(NavHost.kt:85)
at androidx.navigation.compose.NavHostKt$NavHost$32.invoke(NavHost.kt:13)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:45)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:3)
at androidx.compose.animation.AnimatedContentKt$AnimatedContent$6$1$5.invoke(AnimatedContent.kt:2)
at androidx.compose.animation.AnimatedContentKt$AnimatedContent$6$1$5.invoke(AnimatedContent.kt:1)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:45)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:2)
at androidx.compose.animation.AnimatedVisibilityKt.AnimatedEnterExitImpl(AnimatedVisibility.kt:1)
at androidx.compose.animation.AnimatedContentKt$AnimatedContent$6$1.invoke(AnimatedContent.kt:2)
at androidx.compose.animation.AnimatedContentKt$AnimatedContent$6$1.invoke(AnimatedContent.kt:1)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:45)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:1)
at androidx.compose.animation.AnimatedContentKt.AnimatedContent(AnimatedContent.kt:888)
at androidx.navigation.compose.NavHostKt.NavHost(NavHost.kt:1)
at androidx.navigation.compose.NavHostKt.NavHost(NavHost.kt:1)
at com.sample.packagename.MainScreenKt$MainScreenRoute$4$1$3.invoke(MainScreen.kt:200)
at com.sample.packagename.MainScreenKt$MainScreenRoute$4$1$3.invoke(MainScreen.kt:11)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:45)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:2)
at androidx.compose.material3.ScaffoldKt$ScaffoldLayout$1$1$bodyContentPlaceables$1.invoke(Scaffold.kt:2)
at androidx.compose.material3.ScaffoldKt$ScaffoldLayout$1$1$bodyContentPlaceables$1.invoke(Scaffold.kt:1)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:45)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:1)
at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$subcompose$3$1$1.invoke(SubcomposeLayout.kt:2)
at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$subcompose$3$1$1.invoke(SubcomposeLayout.kt:1)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:45)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:1)
at androidx.compose.runtime.ActualJvm_jvmKt.invokeComposable(ActualJvm.jvm.kt:18)
at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:1)
at androidx.compose.runtime.ComposerImpl.composeContent$runtime_release(Composer.kt:14)
at androidx.compose.runtime.CompositionImpl.composeContent(Composition.kt:16)
at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Recomposer.kt:25)
at androidx.compose.runtime.ComposerImpl$CompositionContextImpl.composeInitial$runtime_release(Composer.kt:7)
at androidx.compose.runtime.CompositionImpl.composeInitial(Composition.kt:16)
at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:1)
at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcomposeInto(SubcomposeLayout.kt:15)
at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcompose(SubcomposeLayout.kt:55)
at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcompose(SubcomposeLayout.kt:59)
at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcompose(SubcomposeLayout.kt:169)
at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$Scope.subcompose(SubcomposeLayout.kt:3)
at androidx.compose.material3.ScaffoldKt$ScaffoldLayout$1$1.invoke-0kLqBqw(Scaffold.kt:1)
at androidx.compose.material3.ScaffoldKt$ScaffoldLayout$1$1.invoke(Scaffold.kt:9)
at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$createMeasurePolicy$1.measure-3p2s80s(SubcomposeLayout.kt:113)
at androidx.compose.ui.node.InnerNodeCoordinator.measure-BRTryo0(InnerNodeCoordinator.kt:73)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.kt:2)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.kt:1)
at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:69)
at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.kt:46)
at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:96)
at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:3)
at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui_release(OwnerSnapshotObserver.kt:11)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate.performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:40)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate.access$performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:1)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.remeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:147)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.measure-BRTryo0(LayoutNodeLayoutDelegate.kt:60)
at androidx.compose.foundation.layout.BoxMeasurePolicy.measure-3p2s80s(Box.kt:73)
at androidx.compose.ui.node.InnerNodeCoordinator.measure-BRTryo0(InnerNodeCoordinator.kt:73)
at androidx.compose.ui.graphics.SimpleGraphicsLayerModifier.measure-3p2s80s(GraphicsLayerModifier.kt:1)
at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:45)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.kt:2)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.kt:1)
at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:69)
at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.kt:46)
at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:96)
at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:3)
at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui_release(OwnerSnapshotObserver.kt:11)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate.performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:40)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate.access$performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:1)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.remeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:147)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.measure-BRTryo0(LayoutNodeLayoutDelegate.kt:60)
at androidx.compose.foundation.layout.BoxMeasurePolicy.measure-3p2s80s(Box.kt:73)
at androidx.compose.ui.node.InnerNodeCoordinator.measure-BRTryo0(InnerNodeCoordinator.kt:73)
at androidx.compose.ui.graphics.SimpleGraphicsLayerModifier.measure-3p2s80s(GraphicsLayerModifier.kt:1)
at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:45)
at androidx.compose.foundation.layout.FillNode.measure-3p2s80s(Size.kt:99)
at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:45)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.kt:2)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.kt:1)
at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:69)
at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.kt:46)
at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:96)
at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:3)
at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui_release(OwnerSnapshotObserver.kt:11)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate.performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:40)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate.access$performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:1)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.remeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:147)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.measure-BRTryo0(LayoutNodeLayoutDelegate.kt:60)
at androidx.compose.ui.layout.RootMeasurePolicy.measure-3p2s80s(RootMeasurePolicy.kt:41)
at androidx.compose.ui.node.InnerNodeCoordinator.measure-BRTryo0(InnerNodeCoordinator.kt:73)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.kt:2)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.kt:1)
at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:130)
at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.kt:46)
at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:96)
at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:3)
at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui_release(OwnerSnapshotObserver.kt:11)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate.performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:40)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate.access$performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:1)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.remeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:147)
at androidx.compose.ui.node.LayoutNode.remeasure-_Sx5XlM$ui_release(LayoutNode.kt:20)
at androidx.compose.ui.node.MeasureAndLayoutDelegate.doRemeasure-sdFAvZA(MeasureAndLayoutDelegate.kt:5)
at androidx.compose.ui.node.MeasureAndLayoutDelegate.remeasureOnly(MeasureAndLayoutDelegate.kt:25)
at androidx.compose.ui.node.MeasureAndLayoutDelegate.measureOnly(MeasureAndLayoutDelegate.kt:86)
at androidx.compose.ui.platform.AndroidComposeView.onMeasure(AndroidComposeView.android.kt:106)
at android.view.View.measure(View.java:24726)
at androidx.compose.ui.platform.AbstractComposeView.internalOnMeasure$ui_release(ComposeView.android.kt:64)
at androidx.compose.ui.platform.AbstractComposeView.onMeasure(ComposeView.android.kt:4)
at android.view.View.measure(View.java:24726)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6931)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at android.view.View.measure(View.java:24726)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6931)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:842)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:721)
at android.view.View.measure(View.java:24726)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6931)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at com.android.internal.policy.DecorView.onMeasure(DecorView.java:746)
at android.view.View.measure(View.java:24726)
at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:3184)
at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1952)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2252)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1840)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7937)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:980)
at android.view.Choreographer.doCallbacks(Choreographer.java:804)
at android.view.Choreographer.doFrame(Choreographer.java:739)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:965)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:264)
at android.app.ActivityThread.main(ActivityThread.java:7663)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:980)
        

FYI using KMP purchase version

purchases-kmp = "1.7.1+13.25.0"
# This version must be equal to everything after the '+' in the purchases-kmp version
purchases-common = "13.25.0"
  1. Environment
    1. Platform: Compose Multiplatform
    2. SDK version:
    3. AGP: 8.5.2
    4. Kotlin: 2.1.0
    5. OS version: Sonoma 14.6.1 (23G93)
    6. IDE (e.g. Android Studio, Xcode, Fleet): Android
    7. IDE version: Android Studio Meerkat | 2024.3.1
    8. Device and/or emulator/simulator:
      • Device
      • Emulator/simulator
    9. Environment:
      • Closed testing / Sandbox
      • TestFlight
      • Production
    10. How widespread is the issue. Percentage of devices affected.
  2. Debug logs that reproduce the issue
  3. Steps to reproduce, with a description of expected vs. actual behavior
  4. Other information (e.g. stacktraces, related issues, suggestions how to fix, links for us to
    have context, eg. stackoverflow, etc.)

Additional context
Add any other context about the problem here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions