{"record":{"id":"f064a1d28e1a1832","repo":"microg/GmsCore","slug":"sign-in-result-missing-credential","errorCode":null,"errorMessage":"Sign-in result missing credential","messagePattern":"Sign-in result missing 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":220,"sourceCode":"                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    }\n\n    private fun handleSignInResult(resultCode: Int, data: Intent?) {\n        if (resultCode != RESULT_OK || data == null) {\n            return finishWithGetException(GetCredentialCancellationException(\"Sign-in canceled\"))\n        }\n        runCatching {\n            val bytes = data.getByteArrayExtra(AuthConstants.SIGN_IN_CREDENTIAL)\n                ?: throw IllegalStateException(\"Sign-in result missing credential\")\n            val credential = SafeParcelableSerializer.deserializeFromBytes(bytes, SignInCredential.CREATOR)\n            val credData = Bundle().apply {\n                putString(GOOGLE_ID_BUNDLE_KEY_ID, credential.id)\n                credential.googleIdToken?.let { putString(GOOGLE_ID_BUNDLE_KEY_ID_TOKEN, it) }\n                credential.displayName?.let { putString(GOOGLE_ID_BUNDLE_KEY_DISPLAY_NAME, it) }\n                credential.givenName?.let { putString(GOOGLE_ID_BUNDLE_KEY_GIVEN_NAME, it) }\n                credential.familyName?.let { putString(GOOGLE_ID_BUNDLE_KEY_FAMILY_NAME, it) }\n                credential.profilePictureUri?.let { putString(GOOGLE_ID_BUNDLE_KEY_PROFILE_PICTURE_URI, it.toString()) }\n            }\n            finishWithCredential(TYPE_GOOGLE_ID_TOKEN_CREDENTIAL, credData)\n        }.onFailure { e ->\n            Log.e(TAG, \"handleSignInResult failed\", e)\n            finishWithGetException(GetCredentialUnknownException(e.message ?: \"Sign-in result error\"))\n        }\n    }\n\n    private fun finishWithCredential(type: String, credentialData: Bundle) {\n        val responseBundle = Bundle().apply {","sourceCodeStart":202,"sourceCodeEnd":238,"githubUrl":"https://github.com/microg/GmsCore/blob/157c9d86ac46c195a86c2f15ab55c84036223f95/play-services-core/src/main/kotlin/org/microg/gms/auth/credentials/identity/IdentityCredentialChooserActivity.kt#L202-L238","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nval bytes = data.getByteArrayExtra(AuthConstants.SIGN_IN_CREDENTIAL)\n    ?: throw IllegalStateException(\"Sign-in result missing credential\")\n// after\nval bytes = data.getByteArrayExtra(AuthConstants.SIGN_IN_CREDENTIAL)\nif (bytes == null) {\n    return finishWithGetException(GetCredentialCancellationException(\"Sign-in result missing credential\"))\n}","handlingStrategy":"type-guard","validationCode":"// in onActivityResult, before reading the credential\nval hasCredential = data?.hasExtra(AuthConstants.SIGN_IN_CREDENTIAL) == true","typeGuard":"fun Intent?.signInCredentialBytes(): ByteArray? =\n  this?.takeIf { resultCode == RESULT_OK }?.getByteArrayExtra(AuthConstants.SIGN_IN_CREDENTIAL)","tryCatchPattern":"try {\n  handleSignInResult(resultCode, data)\n} catch (e: IllegalStateException) {\n  finishWithGetException(GetCredentialCancellationException(e.message))\n}","preventionTips":["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"],"tags":["android","credentials","signin","intents"],"backgroundTag":"missing-required-argument","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"}