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
- Check the runCatching failure message "Sign-in result missing credential" in onActivityResult and fall back to GetCredentialCancellationException or a retry
- Ensure the launching flow uses microG's own IdentityCredentialChooserActivity (not a wrapper that swallows or rewrites the result Intent)
- Update microG; older versions had result-intent handling bugs
- 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
- Verify the result intent carries the SIGN_IN_CREDENTIAL extra before deserializing
- Use microG's own chooser activity unmodified so the result contract is preserved
- Keep microG/Play Services versions aligned with the client SDK
- Log result intent extras when debugging sign-in flows
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
- err.errorMessage ?: err.errorCode.toString()
- deleteAll was set to true but keys were also provided
- Element in keys cannot be null or empty
- deleteAll=true but keys are provided
- retrieveAll was set to true but other constraint(s) was also
AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06).
Data as JSON: /api/errors/f064a1d28e1a1832.
Report an issue: GitHub.