microg/GmsCore · warning · RequestHandlingException
NOT_ALLOWED_ERR
NOT_ALLOWED_ERR
Error message
An excluded credential has already been registered with the device
What it means
During CTAP1/U2F registration the handler first runs a CTAP1 authentication command to check whether any of the request's excluded credentials is already on the token. If the device responds (hasCredential is true), the token refuses to re-register and the library throws NOT_ALLOWED_ERR to mirror the authenticator's 'credential excluded' behavior required by WebAuthn.
Source
Thrown at play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/transport/TransportHandler.kt:152
val rpIdHash = options.rpId.toByteArray().digest("SHA-256")
val appIdHash =
options.authenticationExtensions?.fidoAppIdExtension?.appId?.toByteArray()?.digest("SHA-256")
if (!options.registerOptions.parameters.isNullOrEmpty() && options.registerOptions.parameters.all { it.algorithmIdAsInteger != -7 })
throw IllegalArgumentException("Can't use CTAP1 protocol for non ES256 requests")
if (options.registerOptions.authenticatorSelection?.requireResidentKey == true)
throw IllegalArgumentException("Can't use CTAP1 protocol when resident key required")
val hasCredential = options.registerOptions.excludeList.orEmpty().any { cred ->
ctap1DeviceHasCredential(connection, clientDataHash, rpIdHash, cred) ||
if (appIdHash != null) {
ctap1DeviceHasCredential(connection, clientDataHash, appIdHash, cred)
} else {
false
}
}
while (true) {
try {
val response = connection.runCommand(U2fRegistrationCommand(clientDataHash, rpIdHash))
if (hasCredential) throw RequestHandlingException(
ErrorCode.NOT_ALLOWED_ERR,
"An excluded credential has already been registered with the device"
)
require(response.userPublicKey[0] == 0x04.toByte())
val coseKey = CoseKey(
EC2Algorithm.ES256,
response.userPublicKey.sliceArray(1 until 33),
response.userPublicKey.sliceArray(33 until 65),
1
)
val credentialData =
AttestedCredentialData(ByteArray(16), response.keyHandle, coseKey.encode())
val authData = AuthenticatorData(
options.rpId.toByteArray().digest("SHA-256"),
true,
false,
0,
credentialDataView on GitHub (pinned to 157c9d86ac)
Solutions
- Treat this as success-from-the-user's-perspective: the key is already registered — complete the flow without a new registration (WebAuthn InvalidStateError semantics).
- Ask the user to use a different authenticator, or remove the existing credential from the device before re-registering.
- Verify the RP's excludeCredentials list is correct and not duplicating the credential being created.
- Have the user sign in instead of register — the existing credential can authenticate.
Example fix
// before (caller surface)
try {
val resp = handler.register(options, callerPackage)
} catch (e: RequestHandlingException) {
if (e.code == ErrorCode.NOT_ALLOWED_ERR) {
// credential already on this authenticator
toast("This key is already registered — please sign in instead")
}
}
// after
// let the user continue with the existing credential rather than failing the ceremony
val resp = runCatching { handler.register(options, callerPackage) }
.recoverIfRequestHandling(NOT_ALLOWED_ERR) { existingCredentialFallback() } Defensive patterns
Strategy: try-catch
Validate before calling
// pre-flight: run ctap1 check or ask RP to send correct excludeCredentials before registering
Try / catch
try { handler.register(options, pkg) } catch (e: RequestHandlingException) { if (e.code == ErrorCode.NOT_ALLOWED_ERR) completeWithExistingCredential() } Prevention
- Treat NOT_ALLOWED_ERR on register as 'already registered', not a hard failure
- Send accurate excludeCredentials from the RP
- Offer sign-in when the user re-uses a registered key
When it happens
Trigger: ctap1register() (called by register()) detects an existing credential: ctap1DeviceHasCredential/test-of-user-presence loop shows the excluded credentialId is present, so after U2fRegistrationCommand the exception is thrown instead of returning a response.
Common situations: Re-registering a security key that already holds the credential (user re-runs registration for the same account); credentialId list built from stale/duplicate allowList entries; user keeps the same key plugged in while the RP re-enrolls it.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Transport ${transport} not supported
- Attachment ${attachment} not supported
- Attestation conveyance preference ${attachment} not supporte
- No response set.
- PublicKeyCredentialType ${type} not supported
AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06).
Data as JSON: /api/errors/aa0d13081a8a5222.
Report an issue: GitHub.