{"record":{"id":"5e976db44f75b390","repo":"microg/GmsCore","slug":"fido-returned-no-credential","errorCode":null,"errorMessage":"FIDO returned no credential","messagePattern":"FIDO returned no credential","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"play-services-core/src/main/kotlin/org/microg/gms/auth/credentials/identity/IdentityCredentialChooserActivity.kt","lineNumber":194,"sourceCode":"    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {\n        super.onActivityResult(requestCode, resultCode, data)\n        Log.d(TAG, \"onActivityResult: requestCode: $requestCode resultCode: $resultCode\")\n        when (requestCode) {\n            REQ_CODE_FIDO -> handleFidoResult(resultCode, data)\n            REQ_CODE_SIGN_IN -> handleSignInResult(resultCode, data)\n            else -> finishWithGetException(GetCredentialUnknownException(\"Unexpected requestCode=$requestCode\"))\n        }\n    }\n\n    private fun handleFidoResult(resultCode: Int, data: Intent?) {\n        Log.d(TAG, \"handleFidoResult: data: $data\")\n        if (resultCode != RESULT_OK || data == null) {\n            return if (isCreatePath) finishWithCreateException(CreateCredentialUnknownException(\"Passkey flow canceled\"))\n            else finishWithGetException(GetCredentialCancellationException(\"Passkey flow canceled\"))\n        }\n        runCatching {\n            val credentialBytes = data.getByteArrayExtra(FIDO2_KEY_CREDENTIAL_EXTRA)\n                ?: throw IllegalStateException(\"FIDO returned no credential\")\n            val publicKeyCredential = com.google.android.gms.fido.fido2.api.common.PublicKeyCredential\n                .deserializeFromBytes(credentialBytes)\n            (publicKeyCredential.response as? AuthenticatorErrorResponse)?.let { err ->\n                throw IllegalStateException(err.errorMessage ?: err.errorCode.toString())\n            }\n            val json = publicKeyCredential.toJson()\n            val credData = Bundle().apply {\n                putString(if (isCreatePath) PUBKEY_RES_REG_JSON_KEY else PUBKEY_RES_AUTH_JSON_KEY, json)\n            }\n            Log.d(TAG, \"handleFidoResult: $credData\")\n            finishWithCredential(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL, credData)\n        }.onFailure { e ->\n            Log.e(TAG, \"handleFidoResult failed\", e)\n            val msg = e.message ?: \"FIDO result error\"\n            if (isCreatePath) finishWithCreateException(CreateCredentialUnknownException(msg))\n            else finishWithGetException(GetCredentialUnknownException(msg))\n        }\n    }","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/microg/GmsCore/blob/157c9d86ac46c195a86c2f15ab55c84036223f95/play-services-core/src/main/kotlin/org/microg/gms/auth/credentials/identity/IdentityCredentialChooserActivity.kt#L176-L212","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nval credentialBytes = data.getByteArrayExtra(FIDO2_KEY_CREDENTIAL_EXTRA)\n    ?: throw IllegalStateException(\"FIDO returned no credential\")\n// after\nval credentialBytes = runCatching {\n    data.getByteArrayExtra(FIDO2_KEY_CREDENTIAL_EXTRA)\n}.getOrNull() ?: return finishWithGetException(GetCredentialUnknownException(\"FIDO returned no credential\"))","handlingStrategy":"try-catch","validationCode":"if (resultCode != RESULT_OK || data == null) {\n    return finishWithGetException(GetCredentialCancellationException(\"Passkey flow canceled\"))\n}\nval hasCredential = data.hasExtra(FIDO2_KEY_CREDENTIAL_EXTRA)","typeGuard":"fun Intent.hasFidoCredential(): Boolean = hasExtra(FIDO2_KEY_CREDENTIAL_EXTRA)","tryCatchPattern":"runCatching {\n    val bytes = data.getByteArrayExtra(FIDO2_KEY_CREDENTIAL_EXTRA)\n        ?: error(\"FIDO returned no credential\")\n    PublicKeyCredential.deserializeFromBytes(bytes)\n}.onFailure { e -> finishWithGetException(GetCredentialUnknownException(e.message)) }","preventionTips":["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"],"tags":["android","fido2","passkey","intent"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"157c9d86ac46c195a86c2f15ab55c84036223f95","analyzedAt":"2026-09-06T17:27:33.892Z","contentChangedAt":"2026-09-06T17:27:33.892Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}