microg/GmsCore · error · RuntimeException

account is null

Error message

account is null

What it means

requestGellerOauthToken looks up an AccountManager account of the default type matching the given accountName and throws RuntimeException("account is null") if no such account exists on the device. The caller (e.g. getObfuscatedGaiaId) therefore requires a signed-in matching Google account.

Source

Thrown at play-services-core/src/main/kotlin/com/google/android/gms/semanticlocationhistory/extensions.kt:47

const val ODLH_SECURITY_DOMAIN = "users/me/securitydomains/on_device_location_history"
const val ODLH_SECURITY_DOMAIN_SHORT = "on_device_location_history"

const val E2EE_TYPE_URL = "type.googleapis.com/geller.oneplatform.GellerE2eeElement"
const val AES_GCM_IV_SIZE = 12
const val AES_GCM_TAG_BITS = 128
const val AES_KEY_SIZE = 32

const val SEMANTIC_TYPE_HOME = 1
const val SEMANTIC_TYPE_WORK = 2
const val SEARCH_WINDOW_DAYS = 30L
const val SECONDS_PER_DAY = 86400L

suspend fun Context.requestGellerOauthToken(accountName: String, scope: String = "oauth2:https://www.googleapis.com/auth/webhistory"): String {
    val accountManager = AccountManager.get(this)
    val account = accountManager.getAccountsByType(AuthConstants.DEFAULT_ACCOUNT_TYPE).find {
        it.name == accountName
    }
    if (account == null) throw RuntimeException("account is null")
    return withContext(Dispatchers.IO) {
        accountManager.blockingGetAuthToken(account, scope, true)
    } ?: throw RuntimeException("oauthToken is null")
}

suspend fun Context.getObfuscatedGaiaId(accountName: String) = requestGellerOauthToken(accountName, AuthConstants.SCOPE_GET_ACCOUNT_ID)

private val weakCachedKeyMap = WeakHashMap<String, List<Pair<ByteArray, Int>>>()

fun loadKeyMaterials(context: Context, accountName: String): List<Pair<ByteArray, Int>> {
    val result = weakCachedKeyMap.get(accountName)
    if (!result.isNullOrEmpty()) {
        return result
    }
    val keyManager = LocalKeyManager.getInstance(context)
    val keys = keyManager.getKeysForDomain(accountName, ODLH_SECURITY_DOMAIN)
        .ifEmpty { keyManager.getKeysForDomain(accountName, ODLH_SECURITY_DOMAIN_SHORT) }
    val materials = keys.mapNotNull { key ->

View on GitHub (pinned to 157c9d86ac)

Solutions

  1. Verify the account exists before calling: AccountManager.get(context).getAccountsByType(AuthConstants.DEFAULT_ACCOUNT_TYPE).any { it.name == accountName }
  2. Re-query the account list instead of caching the account name across sign-out events
  3. Trigger the account add/sign-in flow if the account is missing
  4. Catch RuntimeException in the caller and prompt for re-authentication

Example fix

// before
val gaiaId = context.getObfuscatedGaiaId(accountName)
// after
val account = AccountManager.get(context).getAccountsByType(AuthConstants.DEFAULT_ACCOUNT_TYPE).find { it.name == accountName }
val gaiaId = if (account != null) context.getObfuscatedGaiaId(accountName) else null
Defensive patterns

Strategy: type-guard

Validate before calling

val exists = AccountManager.get(context)
    .getAccountsByType(AuthConstants.DEFAULT_ACCOUNT_TYPE)
    .any { it.name == accountName }
if (!exists) promptSignIn() else context.getObfuscatedGaiaId(accountName)

Type guard

fun Context.findAccount(accountName: String): Account? =
    AccountManager.get(this).getAccountsByType(AuthConstants.DEFAULT_ACCOUNT_TYPE)
        .firstOrNull { it.name == accountName }

Try / catch

try {
    val gaiaId = context.getObfuscatedGaiaId(accountName)
} catch (e: RuntimeException) {
    handleMissingAccount(accountName)
}

Prevention

When it happens

Trigger: Calling getObfuscatedGaiaId / requestGellerOauthToken with an accountName string that does not exactly match any registered account of AuthConstants.DEFAULT_ACCOUNT_TYPE — account not signed in, typo, different casing, or account removed since it was cached.

Common situations: Semantic location history features running for an account that was signed out or removed from the device, or a stale account name persisted in app settings.

Understand the failure class

Background: "User not found", "Invalid user", and "does not exist": what missing-user lookup errors mean across Rocket.Chat, LiteLLM, Phabricator, rustfs, and pnpm — this error's family across 10 libraries.

Related errors


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