microg/GmsCore · error · RequestHandlingException

INVALID_STATE_ERR

INVALID_STATE_ERR

Error message

null

What it means

ScreenLockTransportHandler.getActiveSignature looks up the requested credential (by rpId and keyId) in the local key store and throws RequestHandlingException(ErrorCode.INVALID_STATE_ERR) when it does not exist. This means the caller asked to sign with a credential ID the screen-lock transport has no record of. It usually indicates stale or inconsistent state between the relying party's stored credentials and the device keystore/database.

Source

Thrown at play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/transport/screenlock/ScreenLockTransportHandler.kt:85

                .setNegativeButtonText(activity.getString(android.R.string.cancel))
                .build()
            invokeStatusChanged(TransportHandlerCallback.STATUS_WAITING_FOR_USER)
            if (signature != null) {
                prompt.authenticate(promptInfo, BiometricPrompt.CryptoObject(signature))
            } else {
                prompt.authenticate(promptInfo)
            }
            continuation.invokeOnCancellation { prompt.cancelAuthentication() }
        }
    }

    suspend fun getActiveSignature(
        options: RequestOptions,
        callingPackage: String,
        keyId: ByteArray
    ): Signature {
        val signature =
            store.getSignature(options.rpId, keyId) ?: throw RequestHandlingException(ErrorCode.INVALID_STATE_ERR)
        showBiometricPrompt(getApplicationName(activity, options, callingPackage), signature)
        return signature
    }

    fun getCredentialData(aaguid: ByteArray, credentialId: CredentialId, coseKey: CoseKey) = AttestedCredentialData(
        aaguid,
        credentialId.encode(),
        coseKey.encode()
    )

    fun getAuthenticatorData(
        rpId: String,
        credentialData: AttestedCredentialData?,
        userPresent: Boolean = true,
        userVerified: Boolean = true,
        signCount: Int = 0
    ) = AuthenticatorData(
        rpId.toByteArray().digest("SHA-256"),

View on GitHub (pinned to 157c9d86ac)

Solutions

  1. Catch RequestHandlingException with ErrorCode.INVALID_STATE_ERR and fall back to a fresh registration flow so a new credential is created.
  2. Remove the stale credential ID from the RP's allowList and re-register the authenticator.
  3. Verify rpId exactly matches the origin used at registration and that the credential was created via this transport on this device.

Example fix

// before
val signature = screenLockHandler.getActiveSignature(options, pkg, keyId)
// after
val signature = try {
    screenLockHandler.getActiveSignature(options, pkg, keyId)
} catch (e: RequestHandlingException) {
    if (e.errorCode == ErrorCode.INVALID_STATE_ERR) registerNewCredential(options)
    else throw e
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
    screenLockHandler.getActiveSignature(options, pkg, keyId)
} catch (e: RequestHandlingException) {
    if (e.errorCode == ErrorCode.INVALID_STATE_ERR) fallBackToRegistration()
    else throw e
}

Prevention

When it happens

Trigger: register() or sign() resolves a keyId and calls getActiveSignature, but store.getSignature(options.rpId, keyId) returns null because the key was deleted, the rpId mismatches, or the credential id came from another device/transport.

Common situations: KeyStore entry cleared (device wipe, app data clear) while the RP still holds the old credential ID; server-side allowList references credentials registered on another device; rpId mismatch after a domain change.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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