Get your free and exclusive +30-page Authentication Analytics Whitepaper

Android & Google Play Services Passkey Error Codes (2026)

Complete reference of Google Play Services passkey error codes (50100-50196) as of GPS v26.06.32. Maps every code to WebAuthn errors.

Vincent Delitz

Vincent

Created: March 5, 2026

Updated: March 5, 2026

Blog-Post-Header-Image

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.

1. Introduction#

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.

StateOfPasskeys Icon

Want to find out how many people use passkeys?

View Adoption Data

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:

  • For web browser errors (DOMExceptions like NotAllowedError and AbortError): see WebAuthn Errors in Production
  • For native deployment strategy and abort rate analysis: see Native App Passkey Errors
  • This article: the GPS error codes, the two API paths that affect error format, practical androidx.credentials error handling and ADB debugging

2. Architecture: how GPS generates and routes Errors#

Every 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.

2.1 Error Generation Pipeline#

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:

  • Codes 50100-50499 (FIDO-layer errors) are passed to a helper that creates a CreatePublicKeyCredentialDomException with the raw message only - no [CODE] prefix.
  • Non-50xxx codes (7, 8, 10, 13, 16, 28xxx) still get the [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.

2.2 Path A: FIDO2 API (direct GPS binding)#

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.

2.3 Path B: Credential Manager API (system broker)#

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.

2.4 Routing Decision#

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:

  1. Android version: Below 14 (API 34) - always FIDO2. The CredentialManager system service does not exist.
  2. Google Play Services version: Must meet a minimum version threshold for Chrome's release channel.
  3. CredentialManager system service availability: Must not be null. Some devices or configurations may not expose the service.
  4. Chrome's internal configuration: Chrome has additional routing logic (in 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.

Debugger Icon

Want to experiment with passkey flows? Try our Passkeys Debugger.

Try for Free

3. Operational Modes: Chrome's Routing as a Case Study#

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 VersionChromeError MessageResult
23.36.1481"The authenticator was previously registered"InvalidStateError
23.36.14145"The authenticator was previously registered"InvalidStateError
26.06.32145"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.

3.1 How Chrome classifies FIDO2 Path Errors#

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?

The same device can use different paths for create() vs get(). On devices in hybrid mode, a 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.

3.2 Confirming the API Path in Chrome's Logs#

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_Fido2CredentialRequestFIDO2 path
  • cr_CredManHelperCredMan path
  • fido_first_party.START in Intent logs → Chrome bound to the FIDO2 privileged service

Example: 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.

4. For native app developers: error handling with 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.

4.1 Exception Types you will actually catch#

When using androidx.credentials, these are the CreateCredentialException types your app encounters in production:

Exception TypeWhenActionable?
CreateCredentialCancellationExceptionUser dismissed the passkey creation promptNo - normal flow, fall back to alternative auth
CreatePublicKeyCredentialDomExceptionWebAuthn-level error. Inspect e.domError.type for the specific DOMExceptionDepends on domError.type
CreateCredentialNoCreateOptionExceptionNo eligible credential provider on the deviceYes - fall back immediately, do not retry
CreateCredentialInterruptedExceptionOperation interrupted by another credential requestYes - retry once
CreateCredentialProviderConfigurationExceptionMissing credentials-play-services-auth dependencyYes - fix your build configuration
CreateCredentialUnknownExceptionCatch-all for unrecognized errorsLog e.type and e.message for diagnostics, then fall back

4.2 DOM Error Types#

When you catch CreatePublicKeyCredentialDomException, inspect e.domError.type to determine the specific WebAuthn error:

domError.typeMeaningAction
"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 levelTreat as cancellation, fall back
"androidx.credentials.TYPE_NOT_SUPPORTED_ERROR"Requested algorithm or feature not supported by the deviceFall back, do not retry
"androidx.credentials.TYPE_SECURITY_ERROR"Domain/origin mismatch - your Digital Asset Links configuration is wrongFix assetlinks.json and RP ID configuration
"androidx.credentials.TYPE_TIMEOUT_ERROR"Ceremony exceeded its timeoutFall back, do not retry

4.3 Error Handling Code#

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.

4.4 What to log for Production Diagnostics#

For every passkey operation failure, log at minimum:

  • Exception class name (e::class.java.simpleName)
  • Exception type (e.type for CreateCredentialException subclasses)
  • DOM error type (e.domError.type for CreatePublicKeyCredentialDomException)
  • Full exception message (e.message)
  • OS version, brand, device model, GPS version
  • Operation type (create vs get)

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.

5. Complete Error Code reference#

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.

5.1 Generation 1: CTAP base (50109-50136)#

The foundation layer (~2019-2022). These codes mirror DOMException values 1:1 and handle the original CTAP/WebAuthn protocol.

CodeWebAuthn EnumMessage(s)What it means
50109NOT_SUPPORTED_ERR"Corp keys are not supported yet."Enterprise/corporate key types are not implemented in GPS
50111INVALID_STATE_ERR(credential already exists / excluded match)A credential matching the excludeCredentials list was found locally
50118SECURITY_ERR(security violation)Security policy violation during the ceremony
50119NETWORK_ERR(network failure during ceremony)Network communication failed mid-operation
50120ABORT_ERR(flow aborted)The FIDO2 flow was aborted by the system
50123TIMEOUT_ERR(ceremony timeout)The WebAuthn ceremony exceeded its timeout
50127ENCODING_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
50128UNKNOWN_ERR(default catch-all)Catch-all for unhandled exceptions. Any GPS failure without a specific code lands here. Transient - retry is appropriate
50129CONSTRAINT_ERR(constraint violation)A WebAuthn constraint was violated
50130DATA_ERR(JSONException / InvalidKeyException)Malformed data in the WebAuthn request or response
50135NOT_ALLOWED_ERR(CredentialManager denied)The Credential Manager denied the operation
50136ATTESTATION_NOT_PRIVATE_ERR(attestation not private)Attestation output does not meet privacy requirements

5.2 Generation 2: core Passkey Operations (50151-50165)#

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.

CodeWebAuthn EnumMessage(s)What it means
50151UNKNOWN_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
50152DATA_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
50153DATA_ERR(MalformedURLException)URL parsing failed during data validation
50154ENCODING_ERR"Ciphertext is too short."
(AEADBadTagException)
Decryption failure. The encrypted credential data is corrupted or the wrong key was used
50155ENCODING_ERR"Failed to encode the private key."
"Failed to encrypt WebauthnCredentialSpecifics."
Key encoding or encryption failed during credential creation or sync
50156NOT_ALLOWED_ERR"Cannot find credential in local KeyStore or database."The passkey referenced by the allowCredentials list does not exist on this device
50157INVALID_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
50158NOT_SUPPORTED_ERR"Insufficient API level: required=24; current=%s."Device runs an Android version below API 24 (Android 7). Passkeys are not supported
50159NOT_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
50160CONSTRAINT_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
50161NOT_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
50162NOT_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
50163NOT_ALLOWED_ERR"Unrecognized key name: %s."
"Failed to sign with Cryptauth."
CryptAuth signing failure - affects credential sync operations
50164NOT_ALLOWED_ERR(dynamic message from BiometricPrompt)Biometric prompt error. The error message comes directly from the device's BiometricPrompt and varies by manufacturer
50165NOT_ALLOWED_ERR"Error deleting/saving/updating/listing key information in SQLite database."GPS's local credential database operation failed. Transient - retry is appropriate
WhitepaperAuthenticationAnalytics Icon

Authentication Analytics Whitepaper:
Track passkey adoption & impact on revenue.

Get Whitepaper

5.3 Generation 3: Screen Lock, Biometric and Activity (50166-50174)#

Device security checks and Android auth integration (~2023).

CodeWebAuthn EnumMessage(s)What it means
50166CONSTRAINT_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
50167NOT_ALLOWED_ERR"Error while accessing KeyStoreHelper."AndroidKeyStore access failed. Can be transient or indicate a corrupted KeyStore
50168ENCODING_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
50169NOT_ALLOWED_ERR(no explicit message)Generic failure
50170DATA_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
50171NOT_ALLOWED_ERR(generic upstream exception wrapper)Wraps an exception from an upstream component
50172NOT_ALLOWED_ERR(FIDO activity result error code)The FIDO2 Activity returned an error result
50173NOT_ALLOWED_ERR(registration/authentication activity wrapper)Activity-level error during registration or authentication
50174NOT_SUPPORTED_ERR"Insufficient API level: required=28; current=%s."Device runs below API 28 (Android 9). Required for certain GPS passkey features

5.4 Generation 4: Credential Manager integration (50175-50179)#

Android 14 CredMan bridge codes (~2024 Q1). These appear when the Credential Manager framework interacts with GPS as a credential provider.

CodeWebAuthn EnumMessage(s)What it means
50175NOT_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
50176NOT_ALLOWED_ERR(no explicit message)Generic CredMan integration failure
50177NOT_SUPPORTED_ERR"None of the algorithms are supported to generate keys"The WebAuthn request specifies key algorithms that GPS does not support
50178NOT_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
50179NOT_ALLOWED_ERR"No certificates found."Certificate validation failed during CredMan operation

5.5 Generation 5: Conditional Create / automatic Passkey Upgrades (50180-50188)#

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.

CodeWebAuthn EnumMessage(s)What it means
50180NOT_ALLOWED_ERR"The request is not valid for creating passkey."The conditional create request failed validation
50181NOT_ALLOWED_ERR"Automatic passkey upgrades is not enabled for preferred acct."The user's preferred account does not have automatic passkey upgrades enabled
50182NOT_ALLOWED_ERR"Automatic passkey upgrades are disabled."The feature is globally disabled (server-side flag)
50183NOT_SUPPORTED_ERR"Attestation formats has no supported formats."No supported attestation format for the conditional create operation
50184NOT_ALLOWED_ERR"No account found for conditional creating the passkey."No matching account exists on the device for the upgrade
50185NOT_ALLOWED_ERR"No password found for the accounts on the device."The conditional create requires an existing password credential - none found
50186NOT_ALLOWED_ERR"No password has same username as passkey to be created."Username mismatch between the existing password and the passkey being created
50187NOT_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
50188INVALID_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.

5.6 Generation 6: Signal API and Sync (50189-50196)#

WebAuthn Level 3 signal API codes (~2025 Q2+). These handle credential lifecycle management via the Signal API and CryptAuth device authorization.

CodeWebAuthn EnumMessage(s)What it means
50189SECURITY_ERR(CryptAuth enrollment timeout - 60s)CryptAuth enrollment took longer than 60 seconds. Network or server issue
50190NOT_ALLOWED_ERR(generic error wrapper)Generic Signal API failure
50191NOT_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
50192NOT_ALLOWED_ERR"Unsupported signal request type"The Signal API request type is not recognized by this GPS version
50193NOT_ALLOWED_ERR"No account found for this signal API."No matching account for the signal operation
50194NOT_ALLOWED_ERR"No passkey listed for this signal API."No passkey matches the signal request
50195NOT_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
50196NOT_ALLOWED_ERR(signal API post-processing failure)Post-processing failed after the signal operation

5.7 Dynamic Error Codes (50100-50108)#

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.

5.8 Non-FIDO Error Codes#

Several error codes outside the 50xxx range affect passkey operations:

Passphrase/Key Retrieval (11xxx)

CodeMessageWhat 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
Substack Icon

Subscribe to our Passkeys Substack for the latest news.

Subscribe

6. Debugging passkey errors on Android with ADB#

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.

6.1 Where Error Fidelity breaks down#

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.

6.2 Why OEM Manufacturer and GPS Version matter#

Two phones running the same Android version can take different API paths based on:

  • GPS APK version: Version codes >= 252400000 use the newer 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.
  • OEM manufacturer: The internal routing logic considers device characteristics. Samsung, Xiaomi and Pixel devices with the same Android version can take completely different paths because the routing applies manufacturer-specific heuristics.
  • Credential provider configuration: Devices with Samsung Pass, 1Password or other third-party providers registered alongside Google Password Manager change the provider landscape. The presence of multiple providers can affect which provider handles the request and how errors are returned.

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.

6.3 Pre-Android 14: the FIDO2-only World#

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.
  • Error messages carry the [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.
  • Hundreds of millions of devices: The legacy FIDO2 API (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.

6.4 ADB Diagnostic Workflow#

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.
  • Zero 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.

6.5 Using AI Coding Agents for ADB Debugging#

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:

  1. Open Settings > About phone on your Android device
  2. Tap Build number 7 times until you see "You are now a developer" (the exact location varies by manufacturer - on some devices it is under Settings > About phone > Software information)
  3. Go back to Settings > System > Developer options and enable USB debugging
  4. Connect the device via USB cable and accept the debugging prompt on the phone

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.

6.6 Diagnosis Checklist#

When you see a spike in generic errors:

  1. Segment by Android version: Is the spike concentrated on pre-14 devices? If yes, every error goes through the FIDO2 path. The [50xxx] codes in native logs or logcat tell you the real cause.
  2. Segment by manufacturer: Error spikes on specific OEMs (e.g. Samsung A-series, Xiaomi Redmi) often indicate a GPS version rollout affecting that manufacturer's devices differently.
  3. Segment by GPS version: A GPS update rolling out to a subset of devices can shift error behavior overnight. Compare error distributions before and after the GPS version change.
  4. Confirm the path via ADB on a representative device: One logcat capture tells you definitively which path is active and what GPS reported internally.
  5. Parse both formats: On the FIDO2 path, errors have the [50xxx] bracket prefix. On the CredMan path, 50xxx codes arrive as raw messages without the prefix. Your parsing logic must handle both.
  6. Use device signals in production: Correlate OS version, GPS version and device model with error rates. Use server-side passkey intelligence to adjust UX based on which error path is likely active for a given device.
Enterprise Icon

Get free passkey whitepaper for enterprises.

Get for free

7. Conclusion#

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 Testimonial

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 Trial

Add passkeys to your app in <1 hour with our UI components, SDKs & guides.

Start Free Trial

Share this article


LinkedInTwitterFacebook