microg/GmsCore · error · IllegalStateException
AcknowledgePurchaseResult purchase item count != 1
Error message
AcknowledgePurchaseResult purchase item count != 1
What it means
parseFrom throws IllegalStateException when the response's purchaseItemData list does not contain exactly one item. The library's contract is that one acknowledge/purchase response corresponds to exactly one purchase item; any other count (0 or >1) is treated as an unexpected server shape and is not recoverable.
Source
Thrown at vending-app/src/main/java/org/microg/vending/billing/core/AcknowledgePurchaseResult.kt:32
response: AcknowledgePurchaseResponse?
): AcknowledgePurchaseResult {
if (response == null) {
throw NullPointerException("response is null")
}
if (response.failedResponse != null) {
return AcknowledgePurchaseResult(
null,
mapOf(
"RESPONSE_CODE" to (response.failedResponse?.statusCode ?: 0),
"DEBUG_MESSAGE" to (response.failedResponse?.msg ?: "")
)
)
}
if (response.purchaseItem == null) {
throw NullPointerException("AcknowledgePurchaseResponse PurchaseItem is null")
}
if (response.purchaseItem?.purchaseItemData?.size != 1)
throw IllegalStateException("AcknowledgePurchaseResult purchase item count != 1")
return AcknowledgePurchaseResult(parsePurchaseItem(response.purchaseItem!!).getOrNull(0))
}
}
}View on GitHub (pinned to 157c9d86ac)
Solutions
- Verify the purchaseToken being acknowledged exists and is in 'purchased' state (query purchases first)
- Check that the app's package name and signing key match what the Play Store knows
- Log purchaseItemData.size to see whether you got 0 or multiple items and handle each case
- Catch IllegalStateException and map it to a billing error code instead of crashing
Example fix
// before
iapCore.acknowledgePurchase(AcknowledgePurchaseParams(purchaseToken = token))
// after
val purchases = billingClient.queryPurchases("inapp")
if (purchases.any { it.purchaseToken == token && it.purchaseState == PURCHASED }) {
iapCore.acknowledgePurchase(AcknowledgePurchaseParams(purchaseToken = token))
} Defensive patterns
Strategy: validation
Validate before calling
val count = ack.purchaseItem?.purchaseItemData?.size
if (count != 1) throw IllegalStateException("Expected 1 purchase item, got $count") Type guard
fun AcknowledgePurchaseResponse.hasSingleItem(): Boolean =
this.purchaseItem?.purchaseItemData?.size == 1 Try / catch
try {
AcknowledgePurchaseResult.parseFrom(ack)
} catch (e: IllegalStateException) {
// log purchaseItemData.size and surface billing error
} Prevention
- Query purchases and confirm state == PURCHASED before acknowledging
- Verify app package name and signing key match Play Store records
- Log item count to distinguish empty vs multi-item responses
When it happens
Trigger: AcknowledgePurchaseResponse.purchaseItem.purchaseItemData.size != 1 — e.g. an empty purchaseItemData list (token not found) or multiple entries from an unexpected server response.
Common situations: Acknowledging an already-consumed or unknown purchase token; backend changes returning multiple purchase items; mismatched app package/signature causing the server to return no items.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
- response is null
- AcknowledgePurchaseResponse PurchaseItem is null
- invalid handle
- INTERNAL_ERROR
- invalid handle
AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06).
Data as JSON: /api/errors/2fbd18d7b83e82ef.
Report an issue: GitHub.