{"record":{"id":"b6eda0ceca437be3","repo":"microg/GmsCore","slug":"invalid-result","errorCode":null,"errorMessage":"Invalid result","messagePattern":"Invalid result","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/ui/hybrid/HybridAuthenticateActivity.kt","lineNumber":177,"sourceCode":"\n        val browserOptions = BrowserPublicKeyCredentialCreationOptions.Builder()\n            .setPublicKeyCredentialCreationOptions(publicKeyCredentialCreationOptions)\n            .setOrigin(\"https://${request.rp.id}\".toUri())\n            .setClientDataHash(request.clientDataHash).build()\n\n        val intent = Intent(this, AuthenticatorActivity::class.java)\n            .putExtra(AuthenticatorActivity.KEY_SOURCE, AuthenticatorActivity.SOURCE_HYBRID)\n            .putExtra(AuthenticatorActivity.KEY_TYPE, AuthenticatorActivity.TYPE_REGISTER)\n            .putExtra(AuthenticatorActivity.KEY_OPTIONS, browserOptions.serializeToBytes())\n\n        val result = suspendCancellableCoroutine { continuation ->\n            waitingLauncherContinuation = continuation\n            waitingLauncher.launch(intent)\n        }\n\n        val resultBytes = result.data?.getByteArrayExtra(FIDO2_KEY_CREDENTIAL_EXTRA) ?: throw RuntimeException(\"No result\")\n        val publicKeyCredential = PublicKeyCredential.deserializeFromBytes(resultBytes)\n        val response = publicKeyCredential.response as? AuthenticatorAttestationResponse? ?: throw RuntimeException(\"Invalid result\")\n        val attestationObject = AttestationObject.decode(response.attestationObject)\n\n        return AuthenticatorMakeCredentialResponse(\n            authData = attestationObject.authData,\n            fmt = attestationObject.fmt,\n            attStmt = attestationObject.attStmt\n        )\n    }\n\n    private suspend fun handleGetAssertion(request: AuthenticatorGetAssertionRequest): AuthenticatorGetAssertionResponse {\n        val publicKeyCredentialRequestOptions = PublicKeyCredentialRequestOptions.Builder()\n            .setRpId(request.rpId)\n            .setChallenge(request.clientDataHash)\n            .setAllowList(request.allowList)\n            .setRequireUserVerification(request.options?.userVerification?.takeIf { it }?.let { UserVerificationRequirement.REQUIRED })\n            .build()\n\n        val browserOptions = BrowserPublicKeyCredentialRequestOptions.Builder()","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/microg/GmsCore/blob/157c9d86ac46c195a86c2f15ab55c84036223f95/play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/ui/hybrid/HybridAuthenticateActivity.kt#L159-L195","documentation":"In HybridAuthenticateActivity.handleMakeCredential, after deserializing the returned PublicKeyCredential, the code attempts a safe cast of its response to AuthenticatorAttestationResponse; if the cast fails (it is not an attestation response) it throws RuntimeException(\"Invalid result\"). The result payload exists but has the wrong response type for a make-credential flow.","triggerScenarios":"The nested activity returned a credential whose response is an AuthenticatorAssertionResponse (get-assertion) instead of AuthenticatorAttestationResponse, or deserialization produced a response of an unexpected subtype.","commonSituations":"Mixing up sign-in and sign-up intents when launching the hybrid flow; a library version where deserializeFromBytes yields a different response class; the inner activity short-circuits with a previously-registered credential.","solutions":["Verify the launched intent requests a make-credential (register) operation, not get-assertion","Log publicKeyCredential.response::class to see the actual type returned","Ensure the FIDO request/options passed to the inner activity are PublicKeyCredentialCreationOptions, not RequestOptions","Update microG so response deserialization matches the request type"],"exampleFix":"// before\nval response = publicKeyCredential.response as? AuthenticatorAttestationResponse? ?: throw RuntimeException(\"Invalid result\")\n// after\nval response = publicKeyCredential.response as? AuthenticatorAttestationResponse\n    ?: run {\n        Log.e(TAG, \"Expected attestation response, got ${publicKeyCredential.response?.javaClass}\")\n        throw RuntimeException(\"Invalid result\")\n    }","handlingStrategy":"type-guard","validationCode":"require(publicKeyCredential.response is AuthenticatorAttestationResponse) {\n    \"Expected attestation response, got ${publicKeyCredential.response?.javaClass}\"\n}","typeGuard":"fun PublicKeyCredential.isAttestation(): Boolean =\n    response is AuthenticatorAttestationResponse","tryCatchPattern":"try {\n    val response = handleMakeCredential()\n} catch (e: RuntimeException) {\n    if (e.message == \"Invalid result\") emit(FidoResult.WrongResponseType) else throw e\n}","preventionTips":["Launch sign-up flows with make-credential options only","Verify the serialized credential version matches between activities","Log the response class when the cast fails","Keep microG FIDO components on matching versions"],"tags":["fido","android","type-mismatch","webauthn"],"backgroundTag":"unexpected-response-shape","analyzedSha":"157c9d86ac46c195a86c2f15ab55c84036223f95","analyzedAt":"2026-09-06T17:27:33.892Z","contentChangedAt":"2026-09-06T17:27:33.892Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}