Complete reference of Google Play Services passkey error codes (50100-50196) as of GPS v26.06.32. Maps every code to WebAuthn errors.
Vincent
Created: March 5, 2026
Updated: March 5, 2026

While investigating a Chrome bug first observed by NTT DOCOMO, we confirmed the issue across our aggregate deployment data and traced it back to how Google Play Services internally routes passkey operations through two different API paths. This article documents the information we gathered during our investigation including the complete GPS error code system.
When a passkey operation fails on Android you often only get a CreatePublicKeyCredentialDomException with exception.domError.type being androidx.credentials.TYPE_NOT_ALLOWED_ERROR. But sometimes error messages contain a [50xxx] prefix that tells you exactly what went wrong - and sometimes they do not. Whether the prefix appears depends on the GPS version, the error type and the API path the device used. Most developers never notice because they only check the exception type, not the raw message. In this article we look at parsing these codes and what actually happens behind the scenes on Android.
Want to find out how many people use passkeys?
Google Play Services (GPS) is the runtime that handles all passkey operations on Android - from key generation and biometric prompts to credential sync and decryption. GPS maintains a system of 50+ error codes (50100-50196) that describe failures with far more precision than the final WebAuthn Error Codes. Chrome as an example receives these codes as a native app but remaps them tightly to WebAuthn DOMExceptions - so websites running inside Chrome never see them to maintain the privacy guarantees set forth by the standards. Native apps that work directly with androidx.credentials can sometimes get more details.
On Android passkey operations can flow through two completely different API paths - the legacy FIDO2 API or the newer Credential Manager API - each with different error handling characteristics. Which path is used depends on the Android version, Chrome version, device manufacturer and credential provider configuration. Understanding these paths is helpful for interpreting error data in production in large scale deployments. Google is migrating Google Password Manager from the FIDO2 API to the Credential Manager framework - a multi-year process. During the transition, different devices are at different stages, which directly affects error behavior.
This reference sheds some light on the FIDO2 error code system as observed in GPS v26.06.32.
What this article covers vs related articles:
NotAllowedError and AbortError): see WebAuthn Errors in Productionandroidx.credentials error handling and ADB debuggingEvery passkey error on Android flows through a pipeline inside Google Play Services before reaching the caller. The caller can be your native app or the Chrome browser (which uses one of the same two paths internally). The critical detail most documentation misses is that these two API paths can have completely different error handling.
Regardless of API path, GPS generates errors the same way internally:
Step 1 - Error code generated: A failure somewhere in GPS produces an error with a code in the 50100-50196 range. For example, code 50162 with message "Can't find proper key to decrypt the private key from WebauthnCredentialSpecifics."
Step 2 - Mapped to WebAuthn ErrorCode: Each 50xxx code maps to one of 12 WebAuthn ErrorCode enum values. Code 50162 maps to NOT_ALLOWED_ERR (value 35).
Step 3 - Message formatted (path-dependent): How the message is formatted depends on which API path delivers it. On the FIDO2 path, all codes are routed through a formatting function that wraps every message as String.format("[%s] %s", code, message) - producing e.g. [50162] Can't find proper key to decrypt the private key from WebauthnCredentialSpecifics. On the Credential Manager path, the behavior splits by code range:
CreatePublicKeyCredentialDomException with the raw message only - no [CODE] prefix.[CODE] prefix on the Credential Manager (CredMan) path.This means [50162] Can't find proper key... appears on the FIDO2 path, but Can't find proper key... (no prefix) appears on the CredMan path.
Step 4 - Delivered to the caller: How the error reaches Chrome or your native app depends on which API path is active.
The caller binds directly to a GPS FIDO2 service. GPS launches its RegistrationActivity or AuthenticationActivity and returns the result as an AuthenticatorErrorResponse containing {errorCode: int, errorMessage: String}.
Chrome and native apps use different service endpoints here. Chrome binds to the privileged fido_first_party.START service, which gives it a trust relationship with GPS - it can act as a WebAuthn relying party for any website without per-origin assetlinks.json. Native apps bind to the standard fido.START service via play-services-fido, which requires Digital Asset Links verification.
Both endpoints return the same AuthenticatorErrorResponse format. Your native app receives the full errorMessage including the [50xxx] prefix and can parse it directly. Chrome, by contrast, runs .equals() string matching on the message in Fido2CredentialRequest.java to remap it to a DOMException. Chrome only pattern-matches 5 known message strings - everything else becomes generic NOT_ALLOWED_ERROR or UNKNOWN_ERROR on the web side.
The caller invokes CredentialManager.createCredential() or getCredential() on the Android system CredentialManager service. The system routes the request to the registered credential provider (GPS, Samsung Pass, etc.). GPS processes the request and returns the result through the system framework.
Chrome and native apps reach the same system service through different API layers. Chrome calls the platform android.credentials.CredentialManager directly - the raw system API available on API 34+. Native apps typically use the Jetpack androidx.credentials wrapper, which adds backward compatibility down to API 23 and provides convenience types like CreatePublicKeyCredentialRequest. On API 34+, the Jetpack wrapper delegates to the same platform service Chrome uses - so both converge on the same system CredentialManager.
Your native app receives a CreateCredentialException or GetCredentialException (Jetpack types) whose message field can contain the [50xxx] prefix. Chrome receives the platform equivalents (android.credentials.CreateCredentialException) and inspects the exception's TYPE string in CredManHelper.java to remap to a DOMException. This type-based matching is more robust than the FIDO2 path's message-based matching because type strings are stable API contracts that don't change when GPS reformats error messages internally.
Available on Android 14+ (API 34+). Google recommends the Jetpack androidx.credentials path for new native integrations.
For both Chrome and native apps using androidx.credentials, the FIDO2 vs CredMan routing is automatic and opaque to the caller. When a native app calls CredentialManager.getCredential() or createCredential(), GPS internally routes based on the installed GPS APK version: version codes >= 252400000 use the new identitycredentials API path, while older versions fall back to the legacy BeginSignIn API - with automatic fallback on failure. The developer has zero control over which internal path GPS takes. This is identical to how Chrome internally routes. The "FIDO2 vs CredMan" choice only exists for developers still using the deprecated com.google.android.gms.fido.fido2 API directly.
For Chrome specifically, which path it uses depends on:
CredManSupportProvider.java) that considers device characteristics. This means two Android 14+ devices from different manufacturers can take completely different paths.The routing decision is cached - it is computed once and reused for subsequent operations.
For navigator.credentials.create() (passkey registration): Chrome routes to either CredMan or FIDO2, never both in parallel. The code checks getBarrierMode() - if it returns ONLY_CRED_MAN, CredMan is used; otherwise, execution falls through to the FIDO2 API.
For navigator.credentials.get() (passkey authentication): Chrome may query both paths in parallel on some configurations, using Barrier.java to coordinate the results.
Native apps using androidx.credentials receive typed exceptions (CreateCredentialException subclasses) with domError.type for WebAuthn-level classification and message fields that may contain the [50xxx] prefix.
Want to experiment with passkey flows? Try our Passkeys Debugger.
For both native apps using androidx.credentials and Chrome, the API path is an opaque internal decision driven by GPS version and device configuration. Understanding Chrome's routing matters if you need to explain why your web analytics show different error distributions than your native logs.
From chromium issue #487944766:
After updating Google Play services, navigator.credentials.create() returns UnknownError instead of InvalidStateError when the user already has a passkey registered.
We confirmed the same regression across our deployment data. The HUAWEI HW-02L (Android 10) demonstrates it clearly:
| GPS Version | Chrome | Error Message | Result |
|---|---|---|---|
| 23.36.14 | 81 | "The authenticator was previously registered" | InvalidStateError |
| 23.36.14 | 145 | "The authenticator was previously registered" | InvalidStateError |
| 26.06.32 | 145 | "An unknown error occurred..." | UnknownError |
Same device, same Chrome version - the only variable is the GPS update from v23 to v26. The error message format changed and Chrome's string matching broke silently.
Chrome's routing logic results in three effective operational modes. The same phone can use different paths for create() vs get(), and two phones running the same Android and Chrome versions can take different paths based on manufacturer and credential provider configuration.
On the FIDO2 path, GPS returns an AuthenticatorErrorResponse with two fields: an errorCode integer (the WebAuthn ErrorCode enum value like INVALID_STATE_ERR = 11) and an errorMessage string. Chrome's convertError() first switches on errorCode, then for certain codes additionally does an exact .equals() match on the message string:
switch (errorCode) { case SECURITY_ERR: return INVALID_DOMAIN; case CONSTRAINT_ERR: if ("The device is not secured with any screen lock".equals(errorMsg)) { return USER_VERIFICATION_UNSUPPORTED; } return UNKNOWN_ERROR; case INVALID_STATE_ERR: if ("One of the excluded credentials exists on the local device".equals(errorMsg)) { return CREDENTIAL_EXCLUDED; } // else fallthrough to UNKNOWN_ERROR case UNKNOWN_ERR: // ... default: return UNKNOWN_ERROR; }
The problem: GPS v26 now formats messages as [50157] One of the excluded credentials exists on the local device. (with prefix and trailing period). Chrome's constant is "One of the excluded credentials exists on the local device" (no prefix, no period). The .equals() fails, INVALID_STATE_ERR falls through to UNKNOWN_ERROR, and the regression from the table above occurs.
On the CredMan path, Chrome matches on exception TYPE strings (TYPE_INVALID_STATE_ERROR) which are stable API contracts - no message parsing involved. This is why the same error is classified correctly on CredMan but broken on FIDO2.
If you see an unexpected UnknownError or NotAllowedError in production, the first diagnostic question is: which path did this device use?
navigator.credentials.create() call uses the FIDO2 API while a navigator.credentials.get() call queries both paths in parallel. This means error behavior can differ between registration and authentication on the same device.You can confirm which path Chrome used on a specific device via logcat. The Chrome-specific log tags to look for:
cr_Fido2Request or cr_Fido2CredentialRequest → FIDO2 pathcr_CredManHelper → CredMan pathfido_first_party.START in Intent logs → Chrome bound to the FIDO2 privileged serviceExample: Samsung Galaxy A16 (Android 15, Chrome 141, GPS 25.42.32)
Logcat showed zero CredentialManager service logs during passkey creation. Chrome bound to fido_first_party.START and logged:
[Fido2CredentialRequest] FIDO2 API call resulted in error: 11 [50157] One of the excluded credentials exists on the local device.
This device - despite running Android 15 - was using the FIDO2 path, not CredMan.
Example: Xiaomi Mi 10 (Android 11, Chrome 137, GPS 25.24.31)
Same behavior: cr_Fido2Request: FIDO2 API call resulted in error: 11 [50157] One of the excluded credentials exists on the local device. Android 11 has no CredentialManager - FIDO2 is the only option.
For the full ADB diagnostic workflow including device profiling, credential provider checks and generalized log capture, see section 6.4.
androidx.credentials#If you are building a native Android app with passkeys, this section is your starting point. Google recommends androidx.credentials for all new implementations. The Credential Manager SDK provides typed exceptions that map to WebAuthn error categories - your error handling branches on exception types and domError.type strings, not on raw error codes. See the Android Credential Manager guide and the androidx.credentials.exceptions API reference for the full exception hierarchy.
When using androidx.credentials, these are the CreateCredentialException types your app encounters in production:
| Exception Type | When | Actionable? |
|---|---|---|
CreateCredentialCancellationException | User dismissed the passkey creation prompt | No - normal flow, fall back to alternative auth |
CreatePublicKeyCredentialDomException | WebAuthn-level error. Inspect e.domError.type for the specific DOMException | Depends on domError.type |
CreateCredentialNoCreateOptionException | No eligible credential provider on the device | Yes - fall back immediately, do not retry |
CreateCredentialInterruptedException | Operation interrupted by another credential request | Yes - retry once |
CreateCredentialProviderConfigurationException | Missing credentials-play-services-auth dependency | Yes - fix your build configuration |
CreateCredentialUnknownException | Catch-all for unrecognized errors | Log e.type and e.message for diagnostics, then fall back |
When you catch CreatePublicKeyCredentialDomException, inspect e.domError.type to determine the specific WebAuthn error:
domError.type | Meaning | Action |
|---|---|---|
"androidx.credentials.TYPE_INVALID_STATE_ERROR" | Duplicate passkey detected (excludeCredentials match) | Inform user they already have a passkey, skip creation |
"androidx.credentials.TYPE_NOT_ALLOWED_ERROR" | Operation not allowed - often a user cancellation at the biometric/system level | Treat as cancellation, fall back |
"androidx.credentials.TYPE_NOT_SUPPORTED_ERROR" | Requested algorithm or feature not supported by the device | Fall back, do not retry |
"androidx.credentials.TYPE_SECURITY_ERROR" | Domain/origin mismatch - your Digital Asset Links configuration is wrong | Fix assetlinks.json and RP ID configuration |
"androidx.credentials.TYPE_TIMEOUT_ERROR" | Ceremony exceeded its timeout | Fall back, do not retry |
The following is a starting point - not production-ready code. The exact exception types and methods may change across androidx.credentials versions, and new error codes are added with GPS updates. To cover the full error surface, test with GPS disabled, screen lock removed, biometrics unenrolled, additional password managers installed and all credential providers turned off. Test on both emulators and physical devices, especially on Android versions below 14 where the CredentialManager system service does not exist.
import androidx.credentials.CreatePublicKeyCredentialRequest import androidx.credentials.CredentialManager import androidx.credentials.exceptions.CreateCredentialException import androidx.credentials.exceptions.CreateCredentialCancellationException import androidx.credentials.exceptions.CreateCredentialInterruptedException import androidx.credentials.exceptions.CreateCredentialNoCreateOptionException import androidx.credentials.exceptions.CreateCredentialProviderConfigurationException import androidx.credentials.exceptions.CreateCredentialUnknownException import androidx.credentials.exceptions.publickeycredential.CreatePublicKeyCredentialDomException suspend fun createPasskey( credentialManager: CredentialManager, context: Context, requestJson: String ) { val request = CreatePublicKeyCredentialRequest(requestJson) try { val result = credentialManager.createCredential(context, request) // Success - process result } catch (e: CreateCredentialCancellationException) { // User dismissed the prompt - normal flow Log.i(TAG, "User cancelled passkey creation") } catch (e: CreatePublicKeyCredentialDomException) { // GPS and AndroidX use "androidx.credentials.TYPE_*" strings. // Third-party providers (Samsung Pass, 1Password, etc.) may use W3C names. // Match both to avoid silent misclassification. when (e.domError.type) { "androidx.credentials.TYPE_INVALID_STATE_ERROR" -> { // Duplicate passkey (excludeCredentials match) Log.i(TAG, "Passkey already exists for this account") } "androidx.credentials.TYPE_NOT_ALLOWED_ERROR" -> { // System-level cancellation or denial Log.w(TAG, "Operation not allowed: ${e.message}") } "androidx.credentials.TYPE_SECURITY_ERROR" -> { // Domain association issue - check assetlinks.json and RP ID Log.e(TAG, "Security error - check assetlinks.json: ${e.message}") } "androidx.credentials.TYPE_NOT_SUPPORTED_ERROR" -> { // Feature not supported on this device Log.w(TAG, "Not supported: ${e.message}") } "androidx.credentials.TYPE_TIMEOUT_ERROR" -> { // Ceremony timed out - fall back, do not retry Log.w(TAG, "Passkey ceremony timed out: ${e.message}") } else -> { Log.w(TAG, "Unhandled DOM error: ${e.domError.type}, ${e.message}") } } } catch (e: CreateCredentialNoCreateOptionException) { // No eligible provider (Error 118) - the most common creation failure. // Fall back immediately - do not retry. Log.w(TAG, "No create options available: ${e.message}") } catch (e: CreateCredentialInterruptedException) { // Interrupted (e.g. user navigated to Settings to enable a provider). // Retry once on resume. Log.w(TAG, "Operation interrupted, retrying: ${e.message}") } catch (e: CreateCredentialProviderConfigurationException) { // Missing credentials-play-services-auth dependency in build Log.e(TAG, "Provider configuration error: ${e.message}") } catch (e: CreateCredentialException) { // Generic catch-all for any other credential exception Log.w(TAG, "Create failed: type=${e.type}, message=${e.message}") } }
The key pattern: catch specific exception types first, handle CreatePublicKeyCredentialDomException with a when on domError.type matching both "androidx.credentials.TYPE_*", and use the generic CreateCredentialException catch as a safety net. Always log e.type and e.message in the generic catch - the message may contain a [50xxx] GPS error code useful for root cause analysis.
For every passkey operation failure, log at minimum:
e::class.java.simpleName)e.type for CreateCredentialException subclasses)e.domError.type for CreatePublicKeyCredentialDomException)e.message)The exception message from GPS sometimes contains a bracketed error code (e.g. [50162]) that identifies the specific failure mode inside Google Play Services. These codes are not part of the public API but appear in the message field and are useful for root cause analysis when investigating error spikes. Note that the [CODE] prefix is not universal: on the CredMan path, 50xxx codes arrive as raw messages without the prefix. Your parsing logic must handle both formats.
The tables below document every known GPS error code, organized by generation (feature area). Each code maps to one WebAuthn ErrorCode enum value and carries one or more error messages. The "What it means" column provides the practical interpretation for native app developers.
The foundation layer (~2019-2022). These codes mirror DOMException values 1:1 and handle the original CTAP/WebAuthn protocol.
| Code | WebAuthn Enum | Message(s) | What it means |
|---|---|---|---|
| 50109 | NOT_SUPPORTED_ERR | "Corp keys are not supported yet." | Enterprise/corporate key types are not implemented in GPS |
| 50111 | INVALID_STATE_ERR | (credential already exists / excluded match) | A credential matching the excludeCredentials list was found locally |
| 50118 | SECURITY_ERR | (security violation) | Security policy violation during the ceremony |
| 50119 | NETWORK_ERR | (network failure during ceremony) | Network communication failed mid-operation |
| 50120 | ABORT_ERR | (flow aborted) | The FIDO2 flow was aborted by the system |
| 50123 | TIMEOUT_ERR | (ceremony timeout) | The WebAuthn ceremony exceeded its timeout |
| 50127 | ENCODING_ERR | "Private key is null." "PublicKey does not support encoding: %s." | Key encoding failed - the generated key material is invalid or uses an unsupported format |
| 50128 | UNKNOWN_ERR | (default catch-all) | Catch-all for unhandled exceptions. Any GPS failure without a specific code lands here. Transient - retry is appropriate |
| 50129 | CONSTRAINT_ERR | (constraint violation) | A WebAuthn constraint was violated |
| 50130 | DATA_ERR | (JSONException / InvalidKeyException) | Malformed data in the WebAuthn request or response |
| 50135 | NOT_ALLOWED_ERR | (CredentialManager denied) | The Credential Manager denied the operation |
| 50136 | ATTESTATION_NOT_PRIVATE_ERR | (attestation not private) | Attestation output does not meet privacy requirements |
The passkey-era operational codes (~2022-2023). Account management, RP ID validation, cryptographic operations and credential lifecycle. This generation includes the critical Folsom sync errors.
| Code | WebAuthn Enum | Message(s) | What it means |
|---|---|---|---|
| 50151 | UNKNOWN_ERR | "The generated key is null." "The generated key has no public key." "The authenticator data is null." "CM launched on Android version below U." "CryptoKey doesn't support signing: %s." "RP-ID Validations are disabled." | State corruption during key generation or CredMan invoked on Android < 14. Multiple unrelated failure modes share this code |
| 50152 | DATA_ERR | "RP ID cannot be validated." "Cannot get package certificate." "The rpId contains a scheme." "rpId must not be empty." "Input origin value doesn't have a scheme" | RP ID validation failed. Your assetlinks.json configuration, RP ID or origin is wrong. Fix your server-side WebAuthn options |
| 50153 | DATA_ERR | (MalformedURLException) | URL parsing failed during data validation |
| 50154 | ENCODING_ERR | "Ciphertext is too short." (AEADBadTagException) | Decryption failure. The encrypted credential data is corrupted or the wrong key was used |
| 50155 | ENCODING_ERR | "Failed to encode the private key." "Failed to encrypt WebauthnCredentialSpecifics." | Key encoding or encryption failed during credential creation or sync |
| 50156 | NOT_ALLOWED_ERR | "Cannot find credential in local KeyStore or database." | The passkey referenced by the allowCredentials list does not exist on this device |
| 50157 | INVALID_STATE_ERR | "One of the excluded credentials exists on the local device." | excludeCredentials match. A passkey for this RP already exists. Chrome detects this via message string matching |
| 50158 | NOT_SUPPORTED_ERR | "Insufficient API level: required=24; current=%s." | Device runs an Android version below API 24 (Android 7). Passkeys are not supported |
| 50159 | NOT_ALLOWED_ERR | "Failed to initialize signature." "Failed to sign." | Cryptographic signing failed - the key material is damaged or the KeyStore is in a bad state |
| 50160 | CONSTRAINT_ERR | "Cannot find an eligible account." "Unable to grab account data from device." | No eligible Google account. The device has no account that can serve as a passkey provider. Not actionable by developers |
| 50161 | NOT_ALLOWED_ERR | "Neither encrypted private key nor encrypted is provided." "No device authorization keys provided to decryption." "No HMAC provided in key data." | Decryption prerequisites are missing - the credential data is incomplete or corrupted |
| 50162 | NOT_ALLOWED_ERR | "Unsuccessful result from folsom activity." "Can't find the proper key to decrypt the private key from WebauthnCredentialSpecifics." "No SharedKey available." "Failed to retrieve Folsom key materials." | Folsom sync key failure. The most common unexpected error in production. A synced passkey exists but the device cannot decrypt its private key. The Google Password Manager sync state is inconsistent. Not actionable by developers - retry or fallback |
| 50163 | NOT_ALLOWED_ERR | "Unrecognized key name: %s." "Failed to sign with Cryptauth." | CryptAuth signing failure - affects credential sync operations |
| 50164 | NOT_ALLOWED_ERR | (dynamic message from BiometricPrompt) | Biometric prompt error. The error message comes directly from the device's BiometricPrompt and varies by manufacturer |
| 50165 | NOT_ALLOWED_ERR | "Error deleting/saving/updating/listing key information in SQLite database." | GPS's local credential database operation failed. Transient - retry is appropriate |

Authentication Analytics Whitepaper:
Track passkey adoption & impact on revenue.
Device security checks and Android auth integration (~2023).
| Code | WebAuthn Enum | Message(s) | What it means |
|---|---|---|---|
| 50166 | CONSTRAINT_ERR | "Screen lock is missing." | No screen lock configured. The device has no PIN, pattern, password or biometric enrolled. Passkeys require user verification. Chrome specifically detects this message |
| 50167 | NOT_ALLOWED_ERR | "Error while accessing KeyStoreHelper." | AndroidKeyStore access failed. Can be transient or indicate a corrupted KeyStore |
| 50168 | ENCODING_ERR | "keyId has invalid size: %s." "KeyHandle's size is too small: %s." "Invalid LocalStorageType in keyHandle: %s." | Key handle parsing failed - the credential data is malformed |
| 50169 | NOT_ALLOWED_ERR | (no explicit message) | Generic failure |
| 50170 | DATA_ERR | "Data missing in intent: authenticationOptions." "Data missing in intent: registrationOptions." | The FIDO2 Activity received an Intent without the required WebAuthn options. Indicates a GPS bug or IPC failure |
| 50171 | NOT_ALLOWED_ERR | (generic upstream exception wrapper) | Wraps an exception from an upstream component |
| 50172 | NOT_ALLOWED_ERR | (FIDO activity result error code) | The FIDO2 Activity returned an error result |
| 50173 | NOT_ALLOWED_ERR | (registration/authentication activity wrapper) | Activity-level error during registration or authentication |
| 50174 | NOT_SUPPORTED_ERR | "Insufficient API level: required=28; current=%s." | Device runs below API 28 (Android 9). Required for certain GPS passkey features |
Android 14 CredMan bridge codes (~2024 Q1). These appear when the Credential Manager framework interacts with GPS as a credential provider.
| Code | WebAuthn Enum | Message(s) | What it means |
|---|---|---|---|
| 50175 | NOT_ALLOWED_ERR | "Failed to parse the assetlinks.json file." "Invalid browser allowlist JSON received." | Digital Asset Links misconfiguration. Your assetlinks.json is malformed or the browser allowlist is invalid |
| 50176 | NOT_ALLOWED_ERR | (no explicit message) | Generic CredMan integration failure |
| 50177 | NOT_SUPPORTED_ERR | "None of the algorithms are supported to generate keys" | The WebAuthn request specifies key algorithms that GPS does not support |
| 50178 | NOT_ALLOWED_ERR | "Invalid response from CredentialManager" "Success response from CM on Create does not contain AuthenticatorAttestationResponse" | The Credential Manager returned an unexpected response format. Likely a GPS or provider bug |
| 50179 | NOT_ALLOWED_ERR | "No certificates found." | Certificate validation failed during CredMan operation |
The conditional create error codes (~2025 Q1-Q2). These handle the automatic password-to-passkey upgrade flow where GPS silently creates a passkey without showing a UI prompt.
| Code | WebAuthn Enum | Message(s) | What it means |
|---|---|---|---|
| 50180 | NOT_ALLOWED_ERR | "The request is not valid for creating passkey." | The conditional create request failed validation |
| 50181 | NOT_ALLOWED_ERR | "Automatic passkey upgrades is not enabled for preferred acct." | The user's preferred account does not have automatic passkey upgrades enabled |
| 50182 | NOT_ALLOWED_ERR | "Automatic passkey upgrades are disabled." | The feature is globally disabled (server-side flag) |
| 50183 | NOT_SUPPORTED_ERR | "Attestation formats has no supported formats." | No supported attestation format for the conditional create operation |
| 50184 | NOT_ALLOWED_ERR | "No account found for conditional creating the passkey." | No matching account exists on the device for the upgrade |
| 50185 | NOT_ALLOWED_ERR | "No password found for the accounts on the device." | The conditional create requires an existing password credential - none found |
| 50186 | NOT_ALLOWED_ERR | "No password has same username as passkey to be created." | Username mismatch between the existing password and the passkey being created |
| 50187 | NOT_ALLOWED_ERR | "No account has a password signed in within the last %d mins." | The conditional create requires a recent password sign-in. The user hasn't signed in with a password recently enough |
| 50188 | INVALID_STATE_ERR | (conditional create state check) | State validation failed during conditional create flow |
Note: conditional create operations cannot be aborted by the user - they either complete silently or fail silently. These errors only appear in server-side telemetry or native app error logging, never as user-facing prompts.
WebAuthn Level 3 signal API codes (~2025 Q2+). These handle credential lifecycle management via the Signal API and CryptAuth device authorization.
| Code | WebAuthn Enum | Message(s) | What it means |
|---|---|---|---|
| 50189 | SECURITY_ERR | (CryptAuth enrollment timeout - 60s) | CryptAuth enrollment took longer than 60 seconds. Network or server issue |
| 50190 | NOT_ALLOWED_ERR | (generic error wrapper) | Generic Signal API failure |
| 50191 | NOT_ALLOWED_ERR | "Unsuccessful result from ReAuth activity." "No DeviceAuthorizationKeys available." "Failed to retrieve device authorization keys." "Cannot find proper decryption key." | Device authorization failure. The device cannot prove its identity for credential sync operations. Related to the Folsom key chain |
| 50192 | NOT_ALLOWED_ERR | "Unsupported signal request type" | The Signal API request type is not recognized by this GPS version |
| 50193 | NOT_ALLOWED_ERR | "No account found for this signal API." | No matching account for the signal operation |
| 50194 | NOT_ALLOWED_ERR | "No passkey listed for this signal API." | No passkey matches the signal request |
| 50195 | NOT_ALLOWED_ERR | "No passkey need to be updated for UnknownCredentialRequest." "No passkey need to be updated for AllAcceptedCredentialsReq." "No passkey need to be updated for CurrentUserDetailsRequest." | The signal operation found no passkeys requiring updates |
| 50196 | NOT_ALLOWED_ERR | (signal API post-processing failure) | Post-processing failed after the signal operation |
Codes 50100-50108 are not statically defined. They are generated dynamically as 50100 + ordinal of the AuthenticatorErrorResponse type. The error message comes from the authenticator's own error string, not from GPS. These appear when the underlying CTAP authenticator itself returns an error.
Several error codes outside the 50xxx range affect passkey operations:
Passphrase/Key Retrieval (11xxx)
| Code | Message | What it means |
|---|---|---|
| 11000 | "Passphrase required." | Google Password Manager's sync passphrase hasn't been entered. Blocks Folsom key retrieval, which cascades to error 50162 |
| 11014 | "Key retrieval required." | Sync key retrieval needed before passkey operations can proceed |
Subscribe to our Passkeys Substack for the latest news.
When generic errors spike in production, the root cause is almost never "an unknown error occurred." GPS generated a specific [50xxx] code - but that code was lost in translation before reaching your analytics. ADB lets you see what GPS actually reported, which API path the device used and why the error was downgraded.
As described in sections 2 and 3, passkey operations flow through either the FIDO2 API or the Credential Manager API. On both paths, the 50+ GPS error codes are compressed into a small set of WebAuthn error types before reaching your app. The CredMan path preserves more detail through typed exceptions, while the FIDO2 path loses granularity earlier. Which path a device uses depends on Android version, GPS version and OEM configuration - so the same GPS error code can produce different exception types on different devices.
Two phones running the same Android version can take different API paths based on:
identitycredentials path internally. Older GPS versions fall back to legacy BeginSignIn. GPS updates roll out gradually - a Samsung A-series in one country may run GPS v25 while the same model in another country already has v26.Error distribution in your analytics is not random noise - it clusters by device model, GPS version and manufacturer. When error rates spike, segment by these dimensions first.
On Android < 14 (API < 34) the CredentialManager system service does not exist. Every passkey operation flows through the legacy FIDO2 API. The Jetpack androidx.credentials library provides backward compatibility down to API 23 but on pre-14 devices it delegates to the old play-services-fido layer internally.
This has direct consequences for error handling:
androidx.credentials still works: The Jetpack library wraps the FIDO2 path internally on pre-14 devices. Your app still receives typed exceptions (CreatePublicKeyCredentialDomException with domError.type etc.) - the underlying API path is transparent.[CODE] prefix: On the FIDO2 path, GPS wraps all messages as String.format("[%s] %s", code, message). The e.message field will contain this prefix, giving you more diagnostic detail than the exception type alone.com.google.android.gms.fido.fido2) is deprecated but still active under the hood. If your user base includes significant pre-14 Android traffic, logging the full message string gives you the [50xxx] code for root cause analysis within the same exception type.When you need to confirm which path a specific device uses and what GPS actually reported, connect via ADB.
Step 1: Identify the device profile
# Android version - need 14+ (API 34+) for CredMan to even be possible adb shell getprop ro.build.version.release adb shell getprop ro.build.version.sdk # OEM manufacturer and model adb shell getprop ro.product.manufacturer adb shell getprop ro.product.model # GPS version - the single most important variable adb shell dumpsys package com.google.android.gms | grep versionName | head -1 # Registered credential providers (only meaningful on API 34+) adb shell settings get secure credential_service adb shell settings get secure credential_service_primary
On pre-14 devices, credential_service returns null - confirming FIDO2 is the only path.
Step 2: Capture the passkey operation
adb logcat -c # ... trigger passkey operation on device ... adb logcat -d | grep -iE "Fido2|CredMan|fido_first_party|FIDO_REGISTRATION|CredentialManager|PublicKeyCredential"
What the logs tell you:
fido_first_party.START or FIDO2-related tags in the output → FIDO2 path. The error message will contain the [50xxx] prefix.CredentialManager service logs → CredMan path. Errors arrive as typed exceptions.CredentialManager logs during the operation → the device did not use CredMan at all, even on Android 14+. This happens when routing heuristics exclude the device based on GPS version or manufacturer.Step 3: Read the actual error
On the FIDO2 path, errors appear as:
FIDO2 API call resulted in error: 11 [50157] One of the excluded credentials exists on the local device.
The 11 is the WebAuthn ErrorCode enum value (INVALID_STATE_ERR). The [50157] is the GPS-internal code that tells you the actual root cause.
On the CredMan path, errors appear as exception type strings like TYPE_INVALID_STATE_ERROR.
Tools like Claude Code or OpenAI Codex can run ADB commands directly once a device is connected - useful for quickly pulling GPS versions, filtering logcat or diagnosing which API path a device uses without memorizing the commands above.
Setting up USB debugging:
Verifying the connection:
adb devices
This should list your device with status device. If it shows unauthorized, accept the USB debugging prompt on the phone.
Once connected, you can ask Claude Code or Codex to run any of the ADB commands above directly - e.g. "check which GPS version is on this device" or "capture logcat during a passkey registration and tell me which API path was used." Copy this article as markdown and paste it into the agent's context so it can cross-reference the output with the error code tables and give you a diagnosis without manual log reading.
When you see a spike in generic errors:
[50xxx] codes in native logs or logcat tell you the real cause.[50xxx] bracket prefix. On the CredMan path, 50xxx codes arrive as raw messages without the prefix. Your parsing logic must handle both.Get free passkey whitepaper for enterprises.
The public SDK exposes 12 WebAuthn error types. Inside GPS, 50+ distinct failure modes are compressed into those 12 types before reaching your app. Chrome remaps these to WebAuthn DOMExceptions to stay within the spec's privacy guidelines, so websites running in Chrome cannot distinguish between a Folsom sync failure and a biometric prompt error - both arrive as NOT_ALLOWED_ERROR.
The accuracy of error classification depends on which API path is active. The CredMan path preserves more detail through typed exceptions, while the FIDO2 path loses granularity earlier. Both paths collapse most GPS error codes into generic errors. The transition between these paths is ongoing and device-dependent - two phones running the same Android version can take different paths based on GPS version, manufacturer and configuration.
If you are shipping passkeys in a native Android app, androidx.credentials gives you typed exceptions and domError.type for primary error handling. For production diagnostics, log the full e.message - it may contain a [50xxx] GPS code that lets you distinguish root causes within the same exception type. The difference between "abort rate spiked 5%" and "error 50162 (Folsom sync failure) grew 7x on Samsung A-series" is the difference between guessing and engineering.
For the web browser error perspective (DOMException names, error messages by browser engine), see WebAuthn Errors in Production. For native deployment strategy and production abort rate data, see Native App Passkey Errors. For setting up your analytics framework, see Passkey Analytics.
Igor Gjorgjioski
Head of Digital Channels & Platform Enablement, VicRoads
Corbado proved to be a trusted partner. Their hands-on, 24/7 support and on-site assistance enabled a seamless integration into VicRoads' complex systems, offering passkeys to 5 million users.
Passkeys that millions adopt, fast. Start with Corbado's Adoption Platform.
Start Free TrialRelated Articles
Table of Contents