microg/GmsCore · error · IllegalStateException

Nothing cached: $cacheKey

Error message

Nothing cached: $cacheKey

What it means

InAppBillingServiceImpl.requestAuthProofToken looks up a previously cached buy-flow entry from buyFlowCacheMap using the caller-supplied cacheKey and throws this IllegalStateException when the key is absent. It means the auth-proof-token request was made without the buy flow first having stored its state under that key.

Source

Thrown at vending-app/src/main/java/org/microg/vending/billing/InAppBillingServiceImpl.kt:149

            buyFlowCacheEntry.lastAcquireResult = coreResult
            if (coreResult.acquireParsedResult.action?.droidGuardMap?.isNotEmpty() == true) {
                DroidGuardClient.getResults(context, "phonesky_acquire_flow", coreResult.acquireParsedResult.action.droidGuardMap).addOnCompleteListener { task ->
                    buyFlowCacheEntry.droidGuardResult = task.result
                }
            }
            coreResult.acquireParsedResult.purchaseItems.forEach {
                PurchaseManager.addPurchase(buyFlowCacheEntry.account, buyFlowCacheEntry.packageName, it)
            }
            return BuyFlowResult(
                coreResult.acquireParsedResult,
                buyFlowCacheEntry.account,
                coreResult.acquireParsedResult.result.toBundle()
            )
        }

        fun requestAuthProofToken(context: Context, cacheKey: String, password: String): String {
            val buyFlowCacheEntry = buyFlowCacheMap[cacheKey]
                ?: throw IllegalStateException("Nothing cached: $cacheKey")
            val deferred = CoroutineScope(Dispatchers.IO).async {
                createIAPCore(
                    context,
                    buyFlowCacheEntry.account,
                    buyFlowCacheEntry.packageName
                ).requestAuthProofToken(password)
            }
            return runBlocking { deferred.await() }
        }

        private fun createIAPCore(context: Context, account: Account, pkgName: String): IAPCore {
            val key = "$pkgName:${account.name}"
            val cacheEntry = iapCoreCacheMap[key]
            if (cacheEntry != null) {
                if (cacheEntry.expiredAt > System.currentTimeMillis())
                    return cacheEntry.iapCore
                iapCoreCacheMap.remove(key)
            }

View on GitHub (pinned to 157c9d86ac)

Solutions

  1. Perform the initial buy-intent/acquire flow that populates buyFlowCacheMap before requesting an auth proof token, and reuse the exact key it returned.
  2. Persist or re-acquire the cache entry if the service process may have restarted (in-memory map is volatile).
  3. Validate the cacheKey on the client side before binding the call.
  4. Handle IllegalStateException in the client and restart the buy flow from the beginning.

Example fix

// before
val token = billing.requestAuthProofToken(context, staleKey, password)
// after
val token = if (hasBuyFlowCache(staleKey)) billing.requestAuthProofToken(context, staleKey, password)
            else runCatching { startBuyFlow(); requestAuthProofToken(context, newKey, password) }
                .getOrThrow()
Defensive patterns

Strategy: validation

Validate before calling

if (!hasBuyFlowCacheEntry(cacheKey)) {
    throw IllegalStateException("Start buy flow first")
}
val token = requestAuthProofToken(context, cacheKey, password)

Try / catch

try {
    val token = requestAuthProofToken(context, cacheKey, password)
} catch (e: IllegalStateException) {
    restartBuyFlow() // cache lost or stale
}

Prevention

When it happens

Trigger: Calling requestAuthProofToken(context, cacheKey, password) with a key never populated by a prior acquire/buy-flow step, or after the cache entry was cleared/evicted, or from a different service instance than the one that populated the map.

Common situations: Client passes a stale cacheKey from an earlier session; buy-flow creation failed earlier but the client proceeded; service process was killed, losing the in-memory cache; key typo/protocol mismatch between caller and service.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06). Data as JSON: /api/errors/807ab8644344b171. Report an issue: GitHub.