microg/GmsCore · error · IllegalStateException

Sign-in result missing credential

Error message

Sign-in result missing credential

What it means

handleSignInResult in IdentityCredentialChooserActivity expects the result Intent to carry a serialized SignInCredential under the SIGN_IN_CREDENTIAL extra. When the extra byte array is absent, an IllegalStateException("Sign-in result missing credential") is thrown inside runCatching and surfaces to the CredentialManager caller as a get/credential retrieval failure.

Source

Thrown at play-services-core/src/main/kotlin/org/microg/gms/auth/credentials/identity/IdentityCredentialChooserActivity.kt:220

                putString(if (isCreatePath) PUBKEY_RES_REG_JSON_KEY else PUBKEY_RES_AUTH_JSON_KEY, json)
            }
            Log.d(TAG, "handleFidoResult: $credData")
            finishWithCredential(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL, credData)
        }.onFailure { e ->
            Log.e(TAG, "handleFidoResult failed", e)
            val msg = e.message ?: "FIDO result error"
            if (isCreatePath) finishWithCreateException(CreateCredentialUnknownException(msg))
            else finishWithGetException(GetCredentialUnknownException(msg))
        }
    }

    private fun handleSignInResult(resultCode: Int, data: Intent?) {
        if (resultCode != RESULT_OK || data == null) {
            return finishWithGetException(GetCredentialCancellationException("Sign-in canceled"))
        }
        runCatching {
            val bytes = data.getByteArrayExtra(AuthConstants.SIGN_IN_CREDENTIAL)
                ?: throw IllegalStateException("Sign-in result missing credential")
            val credential = SafeParcelableSerializer.deserializeFromBytes(bytes, SignInCredential.CREATOR)
            val credData = Bundle().apply {
                putString(GOOGLE_ID_BUNDLE_KEY_ID, credential.id)
                credential.googleIdToken?.let { putString(GOOGLE_ID_BUNDLE_KEY_ID_TOKEN, it) }
                credential.displayName?.let { putString(GOOGLE_ID_BUNDLE_KEY_DISPLAY_NAME, it) }
                credential.givenName?.let { putString(GOOGLE_ID_BUNDLE_KEY_GIVEN_NAME, it) }
                credential.familyName?.let { putString(GOOGLE_ID_BUNDLE_KEY_FAMILY_NAME, it) }
                credential.profilePictureUri?.let { putString(GOOGLE_ID_BUNDLE_KEY_PROFILE_PICTURE_URI, it.toString()) }
            }
            finishWithCredential(TYPE_GOOGLE_ID_TOKEN_CREDENTIAL, credData)
        }.onFailure { e ->
            Log.e(TAG, "handleSignInResult failed", e)
            finishWithGetException(GetCredentialUnknownException(e.message ?: "Sign-in result error"))
        }
    }

    private fun finishWithCredential(type: String, credentialData: Bundle) {
        val responseBundle = Bundle().apply {

View on GitHub (pinned to 157c9d86ac)

Solutions

  1. Check the runCatching failure message "Sign-in result missing credential" in onActivityResult and fall back to GetCredentialCancellationException or a retry
  2. Ensure the launching flow uses microG's own IdentityCredentialChooserActivity (not a wrapper that swallows or rewrites the result Intent)
  3. Update microG; older versions had result-intent handling bugs
  4. Log data.extras keySet() in onActivityResult to confirm what the result intent actually carries

Example fix

// before
val bytes = data.getByteArrayExtra(AuthConstants.SIGN_IN_CREDENTIAL)
    ?: throw IllegalStateException("Sign-in result missing credential")
// after
val bytes = data.getByteArrayExtra(AuthConstants.SIGN_IN_CREDENTIAL)
if (bytes == null) {
    return finishWithGetException(GetCredentialCancellationException("Sign-in result missing credential"))
}
Defensive patterns

Strategy: type-guard

Validate before calling

// in onActivityResult, before reading the credential
val hasCredential = data?.hasExtra(AuthConstants.SIGN_IN_CREDENTIAL) == true

Type guard

fun Intent?.signInCredentialBytes(): ByteArray? =
  this?.takeIf { resultCode == RESULT_OK }?.getByteArrayExtra(AuthConstants.SIGN_IN_CREDENTIAL)

Try / catch

try {
  handleSignInResult(resultCode, data)
} catch (e: IllegalStateException) {
  finishWithGetException(GetCredentialCancellationException(e.message))
}

Prevention

When it happens

Trigger: onActivityResult receives RESULT_OK with an intent whose AuthConstants.SIGN_IN_CREDENTIAL byte-array extra is missing — e.g. the chooser activity finished without putting the credential, or the launch path returned a mismatched result intent.

Common situations: Custom or forked chooser flows forgetting to setResult with the credential extra; inter-process result Intent stripped of extras (large extras or different signing key); mixing microG and Play Services activity result codes.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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