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.

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)

Solutions

  1. Update/replace the FIDO2 credential provider on the device and retry the passkey flow
  2. Ensure the FIDO request is launched with the standard com.google.android.gms.fido.Fido2ApiClient so extras are preserved
  3. Catch the exception inside runCatching (already present) and map it to a CreateCredentialUnknownException / GetCredentialException for the caller
  4. 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

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


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