microg/GmsCore · error · RuntimeException
Network request failed. message=${e.message}
Error message
Network request failed. message=${e.message} What it means
IAPCore.getSkuDetails wraps every exception from the SKU-details network round trip (HTTP call, response decode, result parsing) in a RuntimeException with the message 'Network request failed. message=...'. The original exception type is lost; only its message is preserved.
Source
Thrown at vending-app/src/main/java/org/microg/vending/billing/core/IAPCore.kt:140
val cacheEntry = skuDetailsCache.get(requestBody)
if (cacheEntry != null) {
val getSkuDetailsResult = GetSkuDetailsResult.parseFrom(GoogleApiResponse.ADAPTER.decode(cacheEntry).payload?.skuDetailsResponse)
if (getSkuDetailsResult.skuDetailsList != null && getSkuDetailsResult.skuDetailsList.isNotEmpty()) {
Log.d("IAPCore", "getSkuDetails from cache ")
return getSkuDetailsResult
}
}
Log.d("IAPCore", "getSkuDetails: ")
val response = HttpClient().post(
GooglePlayApi.URL_SKU_DETAILS,
headers = HeaderProvider.getDefaultHeaders(authData, deviceInfo),
payload = skuDetailsRequest,
adapter = GoogleApiResponse.ADAPTER
)
skuDetailsCache.put(requestBody, response.encode())
GetSkuDetailsResult.parseFrom(response.payload?.skuDetailsResponse)
} catch (e: Exception) {
throw RuntimeException("Network request failed. message=${e.message}")
}
}
private fun createAcquireRequest(params: AcquireParams): AcquireRequest {
val theme = 2
val skuPackageName = params.buyFlowParams.skuParams["skuPackageName"] ?: clientInfo.pkgName
val extendedPackageInfo = ExtendedPackageInfo(context, skuPackageName as String)
val docId = if (params.buyFlowParams.skuSerializedDockIdList?.isNotEmpty() == true) {
val sDocIdBytes = Base64.decode(params.buyFlowParams.skuSerializedDockIdList[0], Base64.URL_SAFE + Base64.NO_WRAP)
DocId.ADAPTER.decode(sDocIdBytes)
} else {
val docIdBuilder = DocId.Builder()
docIdBuilder.apply {
backendDocId =
"${params.buyFlowParams.skuType}:$skuPackageName:${params.buyFlowParams.sku}"
type = getSkuType(params.buyFlowParams.skuType)
backend = 3View on GitHub (pinned to 157c9d86ac)
Solutions
- Check device connectivity and that Google Play/microG account is signed in before calling
- Read e.message to identify the underlying cause (status code, null payload, etc.) since the original exception is wrapped
- Catch RuntimeException around getSkuDetails and surface a retryable billing error
- Retry with exponential backoff for transient network failures
Example fix
// before
val details = iapCore.getSkuDetails(params)
// after
val details = try {
iapCore.getSkuDetails(params)
} catch (e: RuntimeException) {
Log.w("Billing", "getSkuDetails failed: ${e.message}")
null
} Defensive patterns
Strategy: try-catch
Validate before calling
if (!isOnline(context)) return
require(accountSignedIn) { "Google account required for getSkuDetails" } Try / catch
try {
iapCore.getSkuDetails(params)
} catch (e: RuntimeException) {
Log.w("Billing", "getSkuDetails failed: ${e.message}")
retryWithBackoff()
} Prevention
- Check connectivity before billing calls
- Ensure the user has a Google account configured in Play/microG
- Remember e.message is the only diagnostic — enable HTTP logging for more detail
- Use exponential backoff retries for transient failures
When it happens
Trigger: Calling getSkuDetails when the HTTP request throws (no network, timeout, non-200 status from HttpClient.get), the protobuf decode fails, or GetSkuDetailsResult.parseFrom throws (null/invalid payload).
Common situations: Device offline or Play Services unreachable; microG not signed in to a Google account; server returning unexpected payload causing parseFrom to throw; HTTP 4xx/5xx from the billing endpoint.
Related errors
AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06).
Data as JSON: /api/errors/dc80de916ca64c81.
Report an issue: GitHub.