Skip to content

Android crash in OpenIapModule.onPurchasesUpdated: IllegalStateException "Already resumed" during requestPurchase flow #94

@edgarrii

Description

@edgarrii

Summary

We're seeing another Android fatal crash in the OpenIAP Google purchase flow.
This one is different from the already fixed ProductManager.getOrQuery crash from #88 / #89.
The crash happens in dev.hyo.openiap.OpenIapModule.onPurchasesUpdated during the requestPurchase flow and looks like another double-resume of a coroutine continuation.

Impact

  • 25 occurrences
  • 25 users impacted
  • fatal crash in production
  • unresolved / ongoing

Environment

  • react-native-iap: 14.7.12
  • OpenIAP Google version resolved by react-native-iap: 1.3.28
  • Android only
  • Seen in production in purchase flow (ProxyBillingActivity visible in app context)

Error

IllegalStateException: Already resumed, but proposed with update []

Stacktrace

at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1133)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:567)
at java.lang.reflect.Method.invoke(Method.java)
at android.app.ActivityThread.main(ActivityThread.java:8641)
at android.os.Looper.loop(Looper.java:313)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Handler.handleCallback(Handler.java:938)
at com.android.billingclient.api.zzan.run(...)
at com.android.billingclient.api.BillingClientImpl.zzV(...)
at dev.hyo.openiap.OpenIapModule.onPurchasesUpdated(...)
at dev.hyo.openiap.OpenIapModule$requestPurchase$1$purchases$1$10$1.invoke(...)
at dev.hyo.openiap.OpenIapModule$requestPurchase$1$purchases$1$10$1.invoke(...)
at kotlinx.coroutines.CancellableContinuationImpl.resumeWith(...)
at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$kotlinx_coroutines_core$default(...)
at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$kotlinx_coroutines_core(...)
at kotlinx.coroutines.CancellableContinuationImpl.b(...)

Suspected root cause

This looks like a separate double-resume race in the purchase flow, not in product querying.

From the current Android OpenIAP purchase logic, requestPurchase stores a continuation callback in something equivalent to:

suspendCancellableCoroutine<List<Purchase>> { continuation ->
  currentPurchaseCallback = { result ->
    if (continuation.isActive) {
      continuation.resume(result.getOrDefault(emptyList()))
    }
  }

  // launch billing flow...
}

Then onPurchasesUpdated(...) later invokes that callback:

currentPurchaseCallback?.invoke(Result.success(mapped))
// ...
currentPurchaseCallback = null

Because currentPurchaseCallback is cleared only after invocation, it seems possible that onPurchasesUpdated(...) may trigger the same callback more than once before it is nulled out, or that multiple billing callbacks race with each other and attempt to resume the same continuation twice.

That would match this crash:

IllegalStateException: Already resumed, but proposed with update []

Expected behavior

The purchase flow should never crash the app if Google Play Billing triggers repeated or late callbacks.

If the continuation has already been resumed or cancelled, subsequent callbacks should be ignored safely.

Actual behavior

The app crashes with a fatal IllegalStateException from CancellableContinuationImpl during purchase handling.

Reproduction notes

We do not yet have a stable local repro, but production data suggests it happens during the purchase flow after Billing launches:

  1. Start requestPurchase
  2. Billing flow opens (ProxyBillingActivity)
  3. onPurchasesUpdated(...) is triggered
  4. The same continuation appears to be resumed more than once
  5. App crashes

Suggested direction

It may help to make the purchase callback single-use before invoking it, for example by capturing and nulling it first:

val callback = currentPurchaseCallback
currentPurchaseCallback = null
callback?.invoke(Result.success(mapped))

or by otherwise guaranteeing that the same continuation cannot be resumed twice even if multiple billing callbacks arrive.

Metadata

Metadata

Assignees

No one assigned

    Labels

    🐛 bugSomething isn't working🤖 androidRelated to android

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions