microg/GmsCore · error · RuntimeException

No result

Error message

No result

What it means

In HybridAuthenticateActivity.handleMakeCredential, the activity waits for a nested authenticator activity via suspendCancellableCoroutine; if the launched Intent result carries no FIDO2_KEY_CREDENTIAL_EXTRA byte array, it throws RuntimeException("No result"). The inner activity returned without producing a credential (typically cancelled or failed).

Source

Thrown at play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/ui/hybrid/HybridAuthenticateActivity.kt:175

            .setExcludeList(request.excludeList)
            .build()

        val browserOptions = BrowserPublicKeyCredentialCreationOptions.Builder()
            .setPublicKeyCredentialCreationOptions(publicKeyCredentialCreationOptions)
            .setOrigin("https://${request.rp.id}".toUri())
            .setClientDataHash(request.clientDataHash).build()

        val intent = Intent(this, AuthenticatorActivity::class.java)
            .putExtra(AuthenticatorActivity.KEY_SOURCE, AuthenticatorActivity.SOURCE_HYBRID)
            .putExtra(AuthenticatorActivity.KEY_TYPE, AuthenticatorActivity.TYPE_REGISTER)
            .putExtra(AuthenticatorActivity.KEY_OPTIONS, browserOptions.serializeToBytes())

        val result = suspendCancellableCoroutine { continuation ->
            waitingLauncherContinuation = continuation
            waitingLauncher.launch(intent)
        }

        val resultBytes = result.data?.getByteArrayExtra(FIDO2_KEY_CREDENTIAL_EXTRA) ?: throw RuntimeException("No result")
        val publicKeyCredential = PublicKeyCredential.deserializeFromBytes(resultBytes)
        val response = publicKeyCredential.response as? AuthenticatorAttestationResponse? ?: throw RuntimeException("Invalid result")
        val attestationObject = AttestationObject.decode(response.attestationObject)

        return AuthenticatorMakeCredentialResponse(
            authData = attestationObject.authData,
            fmt = attestationObject.fmt,
            attStmt = attestationObject.attStmt
        )
    }

    private suspend fun handleGetAssertion(request: AuthenticatorGetAssertionRequest): AuthenticatorGetAssertionResponse {
        val publicKeyCredentialRequestOptions = PublicKeyCredentialRequestOptions.Builder()
            .setRpId(request.rpId)
            .setChallenge(request.clientDataHash)
            .setAllowList(request.allowList)
            .setRequireUserVerification(request.options?.userVerification?.takeIf { it }?.let { UserVerificationRequirement.REQUIRED })
            .build()

View on GitHub (pinned to 157c9d86ac)

Solutions

  1. Check resultCode for RESULT_OK/RESULT_CANCELED before reading the credential extra
  2. Show a cancellation message and re-launch the flow if the user cancelled
  3. Inspect logs of the nested AuthenticatorActivity for failures that end without setting FIDO2_KEY_CREDENTIAL_EXTRA
  4. Catch the RuntimeException in startHybridConnectionFlow and return a cancelable error to the FIDO client

Example fix

// before
val resultBytes = result.data?.getByteArrayExtra(FIDO2_KEY_CREDENTIAL_EXTRA) ?: throw RuntimeException("No result")
// after
if (resultCode != RESULT_OK) throw UserCanceledException()
val resultBytes = result.data?.getByteArrayExtra(FIDO2_KEY_CREDENTIAL_EXTRA)
    ?: throw RuntimeException("No result")
Defensive patterns

Strategy: validation

Validate before calling

val resultBytes = result.data?.getByteArrayExtra(FIDO2_KEY_CREDENTIAL_EXTRA)
if (result == null || resultBytes == null) return null // treat as user-cancel

Type guard

fun Intent?.hasCredentialExtra(): Boolean =
    this?.getByteArrayExtra(FIDO2_KEY_CREDENTIAL_EXTRA) != null

Try / catch

try {
    val response = handleMakeCredential()
} catch (e: RuntimeException) {
    if (e.message == "No result") emit(FidoResult.Canceled) else throw e
}

Prevention

When it happens

Trigger: waitingLauncher.launch(intent) returns a result Intent whose data is null or lacks the FIDO2_KEY_CREDENTIAL_EXTRA extra — i.e. the invoked AuthenticatorActivity finished with RESULT_CANCELED or without a credential payload.

Common situations: User cancelled the hybrid (caBLE/QR) sign-up flow; the inner activity crashed or timed out; the wrong response code path returned a bare result Intent.

Understand the failure class

Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.

Related errors


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