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
- Catch RequestHandlingException with ErrorCode.INVALID_STATE_ERR and fall back to a fresh registration flow so a new credential is created.
- Remove the stale credential ID from the RP's allowList and re-register the authenticator.
- 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
- Verify the credential id exists locally before asserting
- Keep the RP server's credential list in sync with device registrations
- Confirm rpId matches the registration origin exactly
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
- NOT_ALLOWED_ERR
- Transport ${transport} not supported
- Attachment ${attachment} not supported
- Attestation conveyance preference ${attachment} not supporte
- No response set.
AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06).
Data as JSON: /api/errors/a28433460828376d.
Report an issue: GitHub.