microg/GmsCore · error · IllegalStateException
FIDO returned no credential
Error message
FIDO returned no credential
What it means
In IdentityCredentialChooserActivity.handleFidoResult, when the FIDO result intent lacks the FIDO2_KEY_CREDENTIAL_EXTRA byte array, an IllegalStateException("FIDO returned no credential") is thrown. This indicates the FIDO2 flow returned OK but without the serialized PublicKeyCredential payload, which is an unexpected provider response.
Solutions
- Update/replace the FIDO2 credential provider on the device and retry the passkey flow
- Ensure the FIDO request is launched with the standard com.google.android.gms.fido.Fido2ApiClient so extras are preserved
- Catch the exception inside runCatching (already present) and map it to a CreateCredentialUnknownException / GetCredentialException for the caller
- Log the full result intent to identify which provider returned the empty result
Example fix
// before
val credentialBytes = data.getByteArrayExtra(FIDO2_KEY_CREDENTIAL_EXTRA)
?: throw IllegalStateException("FIDO returned no credential")
// after
val credentialBytes = runCatching {
data.getByteArrayExtra(FIDO2_KEY_CREDENTIAL_EXTRA)
}.getOrNull() ?: return finishWithGetException(GetCredentialUnknownException("FIDO returned no credential")) Defensive patterns
Strategy: try-catch
Validate before calling
if (resultCode != RESULT_OK || data == null) {
return finishWithGetException(GetCredentialCancellationException("Passkey flow canceled"))
}
val hasCredential = data.hasExtra(FIDO2_KEY_CREDENTIAL_EXTRA) Type guard
fun Intent.hasFidoCredential(): Boolean = hasExtra(FIDO2_KEY_CREDENTIAL_EXTRA)
Try / catch
runCatching {
val bytes = data.getByteArrayExtra(FIDO2_KEY_CREDENTIAL_EXTRA)
?: error("FIDO returned no credential")
PublicKeyCredential.deserializeFromBytes(bytes)
}.onFailure { e -> finishWithGetException(GetCredentialUnknownException(e.message)) } Prevention
- Launch FIDO flows through the standard Fido2ApiClient only
- Keep Google Play services / microG updated for correct FIDO result extras
- Map empty FIDO results to user-facing unknown-credential errors instead of crashing
When it happens
Trigger: onActivityResult returns RESULT_OK with data, but data.getByteArrayExtra(FIDO2_KEY_CREDENTIAL_EXTRA) is null — the FIDO2 provider activity finished successfully without attaching the credential bytes, e.g. a broken/buggy FIDO provider implementation or an intercepted/interrupted result path.
Common situations: Custom or third-party FIDO2 providers on the device returning malformed results, passkey flows routed through ROM-provided credential managers with incompatible result formats, or race conditions where the result extras were stripped.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
- err.errorMessage ?: err.errorCode.toString()
- A path must start with a single / .
- Access denied, missing google package permission for
- Access denied, missing google package permission or…
- account is null
AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06).
Data as JSON: /api/errors/5e976db44f75b390.
Report an issue: GitHub.
Appendix: source
Thrown at play-services-core/src/main/kotlin/org/microg/gms/auth/credentials/identity/IdentityCredentialChooserActivity.kt:194
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
Log.d(TAG, "onActivityResult: requestCode: $requestCode resultCode: $resultCode")
when (requestCode) {
REQ_CODE_FIDO -> handleFidoResult(resultCode, data)
REQ_CODE_SIGN_IN -> handleSignInResult(resultCode, data)
else -> finishWithGetException(GetCredentialUnknownException("Unexpected requestCode=$requestCode"))
}
}
private fun handleFidoResult(resultCode: Int, data: Intent?) {
Log.d(TAG, "handleFidoResult: data: $data")
if (resultCode != RESULT_OK || data == null) {
return if (isCreatePath) finishWithCreateException(CreateCredentialUnknownException("Passkey flow canceled"))
else finishWithGetException(GetCredentialCancellationException("Passkey flow canceled"))
}
runCatching {
val credentialBytes = data.getByteArrayExtra(FIDO2_KEY_CREDENTIAL_EXTRA)
?: throw IllegalStateException("FIDO returned no credential")
val publicKeyCredential = com.google.android.gms.fido.fido2.api.common.PublicKeyCredential
.deserializeFromBytes(credentialBytes)
(publicKeyCredential.response as? AuthenticatorErrorResponse)?.let { err ->
throw IllegalStateException(err.errorMessage ?: err.errorCode.toString())
}
val json = publicKeyCredential.toJson()
val credData = Bundle().apply {
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))
}
}View on GitHub (pinned to 157c9d86ac)