{"record":{"id":"9702081c0db29686","repo":"microg/GmsCore","slug":"failed-to-retrieve-device-information","errorCode":null,"errorMessage":"Failed to retrieve device information.","messagePattern":"Failed to retrieve device information\\.","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"critical","filePath":"vending-app/src/main/java/org/microg/vending/billing/InAppBillingServiceImpl.kt","lineNumber":171,"sourceCode":"                    buyFlowCacheEntry.account,\n                    buyFlowCacheEntry.packageName\n                ).requestAuthProofToken(password)\n            }\n            return runBlocking { deferred.await() }\n        }\n\n        private fun createIAPCore(context: Context, account: Account, pkgName: String): IAPCore {\n            val key = \"$pkgName:${account.name}\"\n            val cacheEntry = iapCoreCacheMap[key]\n            if (cacheEntry != null) {\n                if (cacheEntry.expiredAt > System.currentTimeMillis())\n                    return cacheEntry.iapCore\n                iapCoreCacheMap.remove(key)\n            }\n            val authData = AuthManager.getAuthData(context, account)\n                ?: throw RuntimeException(\"Failed to obtain login token.\")\n            val deviceEnvInfo = createDeviceEnvInfo(context)\n                ?: throw RuntimeException(\"Failed to retrieve device information.\")\n            val clientInfo = createClient(context, pkgName)\n                ?: throw RuntimeException(\"Failed to retrieve client information.\")\n            val iapCore = IAPCore(context.applicationContext, deviceEnvInfo, clientInfo, authData)\n            iapCoreCacheMap[key] =\n                IAPCoreCacheEntry(iapCore, System.currentTimeMillis() + EXPIRE_MS)\n            return iapCore\n        }\n    }\n\n    private fun getPreferredAccount(extraParams: Bundle?): Account {\n        val name = extraParams?.getString(\"accountName\")\n        name?.let {\n            extraParams.remove(\"accountName\")\n        }\n        return getGoogleAccount(context, name)\n            ?: throw RuntimeException(\"No Google account found.\")\n    }\n","sourceCodeStart":153,"sourceCodeEnd":189,"githubUrl":"https://github.com/microg/GmsCore/blob/157c9d86ac46c195a86c2f15ab55c84036223f95/vending-app/src/main/java/org/microg/vending/billing/InAppBillingServiceImpl.kt#L153-L189","documentation":"createIAPCore calls createDeviceEnvInfo(context) to assemble device environment information required by the IAP backend; a null result triggers this RuntimeException. It means required device metadata (build/environment info) could not be produced, so the IAPCore cannot be constructed.","triggerScenarios":"Any billing API routed through createIAPCore when createDeviceEnvInfo returns null — typically missing system properties/package context needed to build the device environment blob.","commonSituations":"Running in an environment with unusual/missing build fingerprint properties (emulator, custom ROM); microG build mismatch; context lacking expected package info; spoofed-device configuration incomplete.","solutions":["Verify microG's device-spoof/profile configuration is complete and valid.","Run on a device/ROM exposing the standard Android build properties createDeviceEnvInfo reads.","Update microG to a version handling your environment's property set.","Catch the RuntimeException and degrade gracefully (disable billing features) instead of crashing."],"exampleFix":"// before\nval result = billing.getSkuDetails(api, pkg, type, extras)\n// after\nval result = try {\n    billing.getSkuDetails(api, pkg, type, extras)\n} catch (e: RuntimeException) {\n    Log.e(TAG, \"IAP unavailable: ${e.message}\"); null\n}","handlingStrategy":"try-catch","validationCode":"val props = setOf(\"ro.build.fingerprint\", \"ro.product.model\")\nif (props.any { System.getProperty(it).isNullOrBlank() }) failFast(\"Device info incomplete\")","typeGuard":null,"tryCatchPattern":"try {\n    billing.getSkuDetails(...)\n} catch (e: RuntimeException) {\n    if (e.message?.contains(\"device information\") == true) degradeGracefully()\n    else throw e\n}","preventionTips":["Ensure microG device spoof/profile settings are complete","Test on standard ROMs/emulators with typical build properties","Keep microG updated for broader device-info coverage","Gate billing features behind a startup capability check"],"tags":["android","device-info","in-app-billing","microg"],"backgroundTag":"missing-env-var","analyzedSha":"157c9d86ac46c195a86c2f15ab55c84036223f95","analyzedAt":"2026-09-06T17:27:33.892Z","contentChangedAt":"2026-09-06T17:27:33.892Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}