---
url: 'https://www.corbado.com/blog/native-app-passkey-errors'
title: 'Native App Passkey Errors: Ultimate Overview (2026)'
description: 'Native app passkey errors differ drastically between iOS and Android. Learn what breaks, why Android is 3-5x worse and how to measure it.'
lang: 'en'
author: 'Vincent Delitz'
date: '2026-03-05T16:15:50.646Z'
lastModified: '2026-03-25T10:01:37.857Z'
keywords: 'native app passkey errors, passkey errors native apps, Android passkey abort rate, iOS passkey errors, Credential Manager errors, ASAuthorizationError, native passkey analytics, passkey error classification'
category: 'WebAuthn Know-How'
---

# Native App Passkey Errors: Ultimate Overview (2026)

## 1. Introduction

When deploying native apps at large scale with passkeys, difficult problems start
appearing that cannot be anticipated. Why is it so difficult to deploy passkeys in native
apps?

The most important matter: **code cannot be changed once deployed**. One version will
often stay available for a long period. The second matter: **diagnosing problems is
difficult** - you can't easily inspect what's happening inside the user's device. The
third matter: **the environment is constantly changing and extremely heterogeneous** -
your app runs across dozens of OS versions, hundreds of device models, multiple
[authenticator](https://www.corbado.com/glossary/authenticator) configurations and varying network conditions,
all shifting independently while your code stays frozen.

**What this article covers vs related articles:**

- For platform-specific error code references: see
  [Apple Passkey Error Codes](https://www.corbado.com/blog/apple-passkey-error-codes) (iOS) and GPS
  [Passkey Error](https://www.corbado.com/blog/passkey-troubleshooting-solutions) Codes (Android)
- For web browser errors (DOMExceptions like `NotAllowedError` and `AbortError`): see
  [WebAuthn Errors](https://www.corbado.com/blog/webauthn-errors) in Production
- This article: the operational layer - what breaks in native passkey deployments, how to
  measure it and when to act

In this article we answer the following questions:

- What can break - We take apart what can break in a native environment and what it
  encompasses
- How to measure - How should you measure native implementation of passkeys?
- Mitigation - What measures should you take to protect your native application?

## Key Facts

- **Native apps give better error signals than web.** Using
  `preferImmediatelyAvailableCredentials` on iOS and Android, native apps can distinguish
  "no credential available" from "user cancelled" - a signal web browsers deliberately
  hide.
- **Android is 3-5x worse than iOS.** Median abort rates are \~10-14% on Android native vs
  \~2-3% on iOS native. Budget Android devices can reach 75-90%.
- **Code is immutable once deployed.** Unlike web, you cannot hotfix a shipped native app
  version. Error handling bugs persist for months until users update.
- **Events and outcomes are different layers.** An "abort" is a classifier outcome that
  can mean technical failure, user cancel or missing tracking. You need both outcome-level
  KPIs and event-level error logs to act.
- **The implementation choice determines the error surface.** Native implementations
  surface platform-specific error codes (ASAuthorizationError, Credential Manager
  exceptions). WebView implementations surface web DOMExceptions.
- **Android's internal error codes are invisible to web browsers.** GPS compresses 50+
  specific failure modes into 12 generic WebAuthn types before Chrome sees them. Native
  apps can parse the raw error messages to identify root causes. See the full GPS passkey
  error codes reference.
- **Android fragmentation compounds the reliability problem.** It is not the quality - it
  is diversity. A fragmented OS version distribution across thousands of device models,
  combined with different credential provider stacks (Google Password Manager vs Samsung
  Pass vs third-party managers), means a single app ships into dozens of behaviorally
  distinct environments simultaneously.
- **Don't apply iOS thresholds to Android.** A 5% abort rate on iOS may signal a bug. The
  same 5% on Android flagship devices is excellent.

## 2. Passkeys in native Apps

Your implementation architecture determines which error surface you're dealing with. There
are three primary approaches, each producing a different class of errors. For full
implementation guidance, see
[Native App Passkeys](https://www.corbado.com/blog/webauthn-origin-validation-native-apps): Native vs.
[WebView](https://www.corbado.com/blog/native-app-passkeys) Implementation.

|                            | **Native Implementation**                                                                                                                                                                                          | **System WebView**                                        | **Embedded WebView**                                                   |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------- | ---------------------------------------------------------------------- |
| **iOS API**                | `ASAuthorizationController` + `ASAuthorizationPlatformPublicKeyCredentialProvider`                                                                                                                                 | `ASWebAuthenticationSession` / `SFSafariViewController`   | `WKWebView`                                                            |
| **Android API**            | `CredentialManager` + `CreatePublicKeyCredentialRequest` / `GetCredentialRequest`                                                                                                                                  | Chrome Custom Tabs                                        | `android.webkit.WebView` (AndroidX WebKit 1.12.1+)                     |
| **Error surface**          | Platform-specific codes: iOS `ASAuthorizationError` domain codes (see Apple passkey error codes), Android `[50xxx]`-prefixed GPS error messages + Credential Manager exception types (see GPS passkey error codes) | Web DOMExceptions (`NotAllowedError`, `AbortError`, etc.) | Web DOMExceptions + native config failures (AASA, Digital Asset Links) |
| **"No credential" signal** | Clean - `preferImmediatelyAvailableCredentials` returns distinct "no credentials"                                                                                                                                  | Hidden behind opaque `NotAllowedError`                    | Hidden behind opaque `NotAllowedError`                                 |
| **Config failure modes**   | AASA / assetlinks.json                                                                                                                                                                                             | None (runs in browser context)                            | AASA / assetlinks.json + WebKit version + feature detection            |
| **Error guide**            | **This article**                                                                                                                                                                                                   | WebAuthn Errors guide                                     | Both guides apply                                                      |

Many production apps combine approaches: attempt native login first with
`preferImmediatelyAvailableCredentials` for instant authentication, then fall back to
System [WebView](https://www.corbado.com/blog/native-app-passkeys) when no credential is available. This
combination is where the error classification in this article becomes most relevant - you
need to handle both native error codes and web DOMExceptions in the same flow.

The following discussion focuses on **native implementation** errors for
[Android](https://www.corbado.com/blog/how-to-enable-passkeys-android) and [iOS](https://www.corbado.com/blog/webauthn-errors). This
analysis applies whenever you implement either operation:

- **WebAuthn Get** - Login with a credential
- **WebAuthn Create** - Create a new credential

## 3. Environment

Understanding errors in native apps requires tracking the full environment context. Unlike
web browsers where you control the update cycle and users run mostly recent versions,
native apps face a dramatically more complex matrix. Your immutable code runs across a
constantly shifting landscape of OS versions, hardware capabilities and
[authenticator](https://www.corbado.com/glossary/authenticator) states.

### 3.1 Environment Dimensions

The environment for a native passkey implementation consists of eight independent
dimensions that all vary simultaneously:

| Component                             | iOS Example                                                                                   | Android Example                                                                                                | Why it matters                                                                                                                                                                                                                       |
| ------------------------------------- | --------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **OS Version**                        | iOS 16 to iOS 26                                                                              | Android 13 to Android 16                                                                                       | API availability changes (e.g. ASAuthorization behaviors differ between iOS versions, Credential Manager changes)                                                                                                                    |
| **App Version**                       | v2.1.0, v2.2.0, v2.3.1 all active                                                             | v3.0.1, v3.1.0, v3.2.4 all active                                                                              | Bugs shipped in one version persist for 6-12 months until users update. Multiple versions run simultaneously in production                                                                                                           |
| **Device Brand**                      | Apple                                                                                         | Samsung, Google, OnePlus, Xiaomi, Oppo                                                                         | Manufacturer customizations affect WebAuthn behavior. Samsung has different biometric stack than Pixel. Some brands ship heavily modified Android                                                                                    |
| **Device Model**                      | iPhone 16, iPhone 17 Pro, iPhone 17 Pro Max (Face ID)                                         | Pixel 9/10 Pro, Samsung S24 (SM-S921B), Galaxy A15 (SM-A156E), OPPO CPH2695                                    | Biometric hardware affects error patterns. Low-end devices have unreliable sensors. Specific models have known quirks                                                                                                                |
| **Authenticator Status**              | iCloud Keychain enabled/disabled, sync status, iCloud signed in                               | Google Password Manager enabled, Play Services version, third-party providers registered                       | Availability unpredictable.                                                                                                                                                                                                          |
| **Default / Preferred Authenticator** | iOS 17+: iCloud Keychain or third-party (1Password, Dashlane) if selected in Settings         | Android 14+: Google Password Manager or third-party (Bitwarden, 1Password) if registered and active            | Third-party providers can be installed but deactivated. Operations fail differently when preferred provider unavailable vs when feature not supported pre-iOS 17/Android 14. Some manufacturers disable third-party support entirely |
| **Device Protection**                 | Passcode required, Face ID enrolled, Touch ID enrolled, passcode-only mode, biometric lockout | PIN/Pattern/Password required, fingerprint enrolled, face unlock enrolled, screen lock only, biometric lockout | Passkeys require device protection configured (passcode/PIN/gesture OR biometric). If neither configured, passkeys not supported. Lockout state affects which errors surface                                                         |

### 3.2 Android Diversity Problem - why Android breaks more than iOS

The environmental complexity described above creates dramatically different outcomes on
[Android](https://www.corbado.com/blog/how-to-enable-passkeys-android) vs [iOS](https://www.corbado.com/blog/webauthn-errors).
**Android's extreme heterogeneity makes it 3-5x more susceptible to passkey implementation
failures** compared to [iOS](https://www.corbado.com/blog/webauthn-errors) in production environments.

#### 3.2.1 Error Rate Distribution by Platform

When measuring abort rates across native passkey implementations, the distribution
consistently reveals the [Android](https://www.corbado.com/blog/how-to-enable-passkeys-android) diversity problem
across virtually every deployment we have data or reports on:

| Platform           | Best Case                           | Median |
| ------------------ | ----------------------------------- | ------ |
| **iOS Native**     | 0-3% (flagship models, recent iOS)  | 2-3%   |
| **Android Native** | 0-2% (Pixel, premium OPPO/Motorola) | 10-14% |

**Key insight**: The worst iOS device performs better than the median Android device. iOS
maintains consistency across its ecosystem because Apple controls both hardware and
software. [Android's](https://www.corbado.com/blog/how-to-enable-passkeys-android) open ecosystem creates massive
variance.

#### 3.2.2 Brand-Level Performance Variance

Android error rates cluster strongly by manufacturer, revealing how deeply customization
affects reliability:

| Manufacturer Tier                               | Typical Abort Range | Reason                                                                   |
| ----------------------------------------------- | ------------------- | ------------------------------------------------------------------------ |
| **Google Pixel**                                | 3-15%               | Stock Android, tight Credential Manager integration, consistent hardware |
| **Premium Android** (Samsung S-series flagship) | 8-12%               | Manufacturer customization balanced with quality control                 |
| **Budget Android** (Samsung A-series)           | 20-40%              | Cost-optimized hardware, unreliable biometric sensors                    |
| **Low-tier Budget** (ZTE, vivo budget)          | 75-90%              | Credential Manager implementation effectively broken on some models      |

For comparison, **iOS devices cluster tightly** around 2-5% regardless of model, with
outliers only appearing on very old hardware (iPhone 8/X era) running outdated iOS
versions.

#### 3.2.3 Authentication Method Gap

Device protection type amplifies the Android fragmentation problem:

| Platform    | Biometric Auth | PIN/Code Auth | Code Penalty         |
| ----------- | -------------- | ------------- | -------------------- |
| **iOS**     | baseline       | 1.5-2x worse  | moderate degradation |
| **Android** | baseline       | 2-3x worse    | severe degradation   |

**Why this matters**: On Android budget devices, PIN/code authentication can push abort
rates from 30% to 60%+. The same operation on iOS might go from 3% to 6% - still usable.

#### 3.2.4 Manufacturer Customization Impact

The root cause is architectural:

**iOS**: Apple controls the entire ASAuthorization stack.
[iCloud Keychain](https://www.corbado.com/glossary/icloud-keychain) integration is identical on iPhone 16 and
iPhone 17 Pro. Updates are synchronized. Biometric hardware (Face ID, Touch ID) is
standardized per generation.

**Android**: Manufacturers customize everything:

- [Samsung's](https://www.corbado.com/blog/samsung-passkeys) biometric stack differs from Google's
- Some manufacturers (OnePlus, Xiaomi, Oppo) ship heavily modified Android builds
- Google Play Services version variance affects Credential Manager availability
- Budget device manufacturers optimize for cost, not reliability
- Third-party provider support is **optional** - manufacturers can disable it entirely

This means an error on a [Samsung](https://www.corbado.com/blog/samsung-passkeys) Galaxy A15 running
[Android 15](https://www.corbado.com/blog/android-15-passkeys-single-tap-autofill-fallback-ui) tells you nothing
about what will happen on a Pixel 9 running the same Android version. The codepaths are
different.

#### 3.2.5 Practical Implications for Error Classification

When you see high abort rates:

**On iOS**:

- Likely a code bug (your implementation is broken)
- Or user behavior pattern (users actually canceling - `ASAuthorizationErrorCanceled`
  code 1001)
- Or environmental issue (iCloud Keychain disabled/not signed in - surfaces as
  `ASAuthorizationErrorFailed` code 1004 wrapping an internal "Cannot
  [create a passkey](https://www.corbado.com/blog/passkey-creation-best-practices).
  [iCloud Keychain](https://www.corbado.com/glossary/icloud-keychain) is off." error, or the new
  `ASAuthorizationError.deviceNotConfiguredForPasskeyCreation` code 1010 on
  [iOS 26](https://www.corbado.com/blog/ios-26-passkeys)+)

**On Android**:

- Could be code bug
- Could be user behavior
- Could be manufacturer customization breaking Credential Manager
- Could be budget device with broken biometric hardware
- Could be OS version too old for stable Credential Manager
- Could be device manufacturer disabled third-party provider support
- Could be Google Play Services version incompatibility

**Bottom line**: Don't use the same error rate thresholds for Android and iOS. A 5% abort
rate on iOS might indicate a problem. The same 5% on Android flagship devices is
excellent. On Android budget devices, anything under 30% is acceptable. For testing these
device-specific behaviors systematically, see Testing Passkey Flows in Native iOS &
Android Apps.

## 4. What actually breaks in native Passkey Flows

If you want to diagnose passkey issues in native apps, you need to separate **where** the
failure happened from **how** it was classified later.

The following diagram shows the full lifecycle of a native passkey registration (create)
operation. The flow applies to both Android (Credential Manager → TEE) and iOS
(ASAuthorizationController → [Secure Enclave](https://www.corbado.com/glossary/secure-enclave)). Each numbered
step represents a boundary where failures can occur - from the app through the OS
framework, down to secure hardware and back to the server:

```mermaid
flowchart LR

  subgraph CLIENT["Client Device"]
    direction TB

    subgraph APP_SPACE["App Space (User Space)"]
      U["1. User starts registration"]
      APP["2. App + Passkey SDK"]
      PAYLOAD["7. Build registration payload"]
      E1["11. Expected error (user cancelled)"]
      E2["12. Unexpected error (pre/during auth)"]
      E3["13. Unexpected error (post-auth)"]
      U --> APP
    end

    subgraph FRAMEWORK["OS Framework Services"]
      CM["4. Credential Manager / ASAuthorization"]
      BIO["5. Biometric or Device Credential UI"]
    end

    subgraph KSPACE["OS Kernel Space"]
      KERN["5b. Kernel key operation path"]
    end

    subgraph HWSEC["Secure Hardware Boundary"]
      TEE["5c. TEE / Secure Enclave"]
    end

    CRASH{{"14. App Crash"}}
  end

  subgraph SERVER["Passkey Server"]
    SS["3. Start returns attestation options"]
    SF["9. Finish - verify and store passkey"]
  end

  APP -- "2b. HTTPS request start" --> SS
  SS -- "3b. Attestation options" --> APP

  APP --> CM
  CM --> BIO
  BIO --> KERN
  KERN --> TEE
  TEE --> KERN
  KERN --> CM
  CM --> APP

  APP --> PAYLOAD
  PAYLOAD -- "8. HTTPS request finish" --> SF
  SF -- "10. Success response" --> APP

  SS -. "3c. Failed to generate options" .-> E2
  CM -. "11a. User cancelled" .-> E1
  CM -. "11b. Credential Manager error" .-> E2
  CM -. "12a. Provider or API failure" .-> E2
  KERN -. "12b. Key operation failure" .-> E2
  SF -. "13a. Verification or storage failed" .-> E3
  CM -. "14a. Unhandled exception" .-> CRASH

  style E2 fill:#4a1a1a,stroke:#ff6b6b,stroke-width:2px,color:#ff6b6b
  style E3 fill:#4a1a1a,stroke:#ff6b6b,stroke-width:2px,color:#ff6b6b
  style CRASH fill:#4a1a1a,stroke:#ff6b6b,stroke-width:2px,color:#ff6b6b

  linkStyle 13 stroke:#ff6b6b,stroke-width:2px
  linkStyle 15 stroke:#ff6b6b,stroke-width:2px
  linkStyle 16 stroke:#ff6b6b,stroke-width:2px
  linkStyle 17 stroke:#ff6b6b,stroke-width:2px
  linkStyle 18 stroke:#ff6b6b,stroke-width:2px
  linkStyle 19 stroke:#ff6b6b,stroke-width:2px
```

In practice, native WebAuthn failures happen in five stages:

| Stage                    | What happens                                          | Typical failure source                                                             | Best telemetry signal                                                                                                |
| ------------------------ | ----------------------------------------------------- | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| Pre-authenticator        | Build request, fetch challenge/options, fetch token   | API/network/serialization/state issues                                             | Unexpected error events with pre-authenticator context                                                               |
| Authenticator invocation | System sheet/dialog is shown and user/device responds | User cancel, no credential, platform/provider exception                            | Platform error codes: Apple passkey error codes (iOS), GPS passkey error codes (Android), WebAuthn errors (browsers) |
| Post-authenticator       | Server-side verification / finish call                | Backend rejection, timeout, transport failures                                     | Server-side error codes from verification endpoint                                                                   |
| User intent path         | User declines the passkey path in app UI              | Product UX choice, not technical failure                                           | Abort tracking in your analytics                                                                                     |
| App crash                | Authenticator response handling crashes the app       | Unhandled exception during credential parsing, malformed response, threading issue | Crash reporting (e.g. Crashlytics, Sentry) correlated with passkey operation                                         |

On both platforms, the [authenticator](https://www.corbado.com/glossary/authenticator) invocation stage is where
native apps have a decisive diagnostic advantage over web browsers. Both Apple and Google
compress dozens of specific internal error codes into a small set of public error types
before they reach your app - and browsers lose even more detail on top of that. Native
apps that log the raw platform errors can distinguish root causes that browsers collapse
into generic `NotAllowedError`.

On **Android**, GPS generates 50+ internal error codes that are compressed into 12
WebAuthn types. Native apps can parse the `[50xxx]` prefixed error messages to identify
specific failures like Folsom sync key decryption (error 50162) that Chrome reports as
generic `NOT_ALLOWED_ERROR`. For the complete error code reference, architecture diagrams
and `androidx.credentials` error handling guide, see GPS
[Passkey Error](https://www.corbado.com/blog/passkey-troubleshooting-solutions) Codes: the complete Reference.

On **iOS**, Apple compresses \~22 internal error codes into 11 public
`ASAuthorizationError` codes. Native apps receive dedicated codes for common failures
(1001 for user cancel, 1005 for no credentials, 1010 for device not configured) that
Safari collapses into generic DOMExceptions. For the complete error code reference,
translation mapping and Swift error handling guide, see
[Apple Passkey Error Codes](https://www.corbado.com/blog/apple-passkey-error-codes): the complete Reference.

|                              | **iOS**                                                       | **Android**                                           |
| ---------------------------- | ------------------------------------------------------------- | ----------------------------------------------------- |
| **Internal error codes**     | \~22 (`ASCAuthorizationError` 0-22)                           | 50+ (GPS codes 50100-50196)                           |
| **Public error codes**       | 11 (`ASAuthorizationError` 1000-1010)                         | 12 WebAuthn `ErrorCode` enum values                   |
| **Key diagnostic mechanism** | `localizedDescription` / `NSUnderlyingErrorKey` on code 1004  | `[50xxx]` prefix in error message string              |
| **Platform trend**           | Apple extracting specifics from generic 1004 each iOS release | Google migrating FIDO2 path → Credential Manager path |
| **Browser information loss** | Safari translates to \~3 DOMException types                   | Chrome pattern-matches on 5 known message strings     |
| **Full reference**           | Apple passkey error codes                                     | GPS passkey error codes                               |

Beyond these stages, two behavioral patterns are critical to detect:

**Authentication loop detection**: When the authenticator invocation fails and error
handling is incorrect, the app can enter a retry loop - repeatedly invoking the
authenticator, crashing or failing each time. This is especially dangerous in native apps
because the user has no way to break out except force-closing the app. At Corbado, we
detect these loops server-side by tracking repeated failed attempts from the same session
within a short time window. A loop pattern (3+ failed attempts within a few minutes) is a
strong signal that the app's error handling is broken on that specific device/OS
combination.

**Authentication avoided by device change**: When a user consistently authenticates on one
device but suddenly switches to another (e.g. moving from
[native app](https://www.corbado.com/blog/native-app-passkeys) to web browser, or from one phone to another),
this often signals a problem. The user is actively working around a broken passkey flow on
their primary device. Tracking device-change patterns after failed authentication attempts
reveals which device/OS/version combinations are silently pushing users away from
passkeys - even when no error was reported.

## 5. How Corbado can help

Without dedicated tooling, teams typically discover native passkey problems reactively - a
support ticket spike, a sudden drop in
[login conversion](https://www.corbado.com/blog/login-friction-kills-conversion) or a vague "passkeys don't work
on my phone" report. By the time you investigate, the damage is done: users have churned,
fallback flows have been triggered silently and the root cause is buried in aggregate
metrics.

Corbado's [passkey analytics](https://www.corbado.com/blog/passkey-analytics) platform continuously monitors
device-level passkey health across your entire user base - both for
[passkey creation](https://www.corbado.com/blog/passkey-creation-best-practices) and
[passkey login](https://www.corbado.com/blog/passkey-login-best-practices). Instead of waiting for complaints,
you see which device models are failing, how error rates trend over time and whether a new
OS update or GPS version introduced regressions. Effective native
[passkey analytics](https://www.corbado.com/blog/passkey-analytics) needs two layers working together:
**outcomes** that tell you "how bad is the user impact?" and **events** that tell you
"what is technically failing?" - the sections below walk through both. For the full
analytics framework, see [Passkey Analytics](https://www.corbado.com/blog/passkey-analytics) and the
[Authentication Analytics](https://www.corbado.com/blog/authentication-analytics-playbook) Playbook. The
screenshots below show sample data from a demo environment for illustration purposes.

### 5.1 Device Health Monitoring for Passkey Creation

The first layer of monitoring is the device health view for
[passkey creation](https://www.corbado.com/blog/passkey-creation-best-practices). This surfaces the "top
offenders" - device models with the highest abort rates during passkey registration -
ranked by error percentage and with per-device event counts for statistical confidence.

![Device health: top offending iOS devices for passkey creation](https://s3.eu-central-1.amazonaws.com/corbado-cloud-staging-website-assets/device_health_bd7211b105.png)

On iOS, the dashboard immediately reveals patterns that would take weeks to discover
manually: older devices stuck on iOS 16 (iPhone 8 Plus, iPhone X, iPhone 8) cluster at the
top with 20%+ abort rates, while modern devices on
[iOS 18](https://www.corbado.com/blog/ios-18-passkeys-automatic-passkey-upgrades)+ sit around 10-12%. The
**Block** button lets you suppress
[passkey creation](https://www.corbado.com/blog/passkey-creation-best-practices) prompts for specific device
cohorts where the failure rate is unacceptable -
[Passkey Intelligence](https://docs.corbado.com/corbado-connect/features/passkey-intelligence)
then skips passkey creation entirely on those devices instead of leading users into a dead
end.

### 5.2 Error Classification for deep Investigation

Device health tells you **where** things break. The error classification view tells you
**what** is breaking - grouping individual errors into named patterns with counts, rates
and trend indicators.

![Error classification: passkey creation error patterns with counts and trends](https://s3.eu-central-1.amazonaws.com/corbado-cloud-staging-website-assets/error_classification_d6ee7ec81a.png)

This is where you move from "Android has a higher abort rate" to actionable engineering
work. The trend percentages are the key signal, here just some exemplary sample data in
production there are over 100 classifications: While the top pattern (authenticator
timeout) is declining at -9%, the iOS native unknown errors are growing at +25% and
Android native at +37%. A
[Bitwarden](https://www.corbado.com/blog/passkey-analysis-bitwarden-developer-survey-2024) Chrome extension bug
has spiked +205% - likely triggered by a recent extension update. The same drill-down
workflow applies to any pattern in the list - the native iOS and Android errors visible
here work exactly the same way. We follow the
[Bitwarden](https://www.corbado.com/blog/passkey-analysis-bitwarden-developer-survey-2024) spike below because it
had the largest trend change, but investigating a native `ASAuthorizationError` or
Credential Manager failure uses the same path. Clicking into any pattern opens a detailed
view where you can analyze how the error developed over time, see it in context of total
login volume and inspect individual error samples:

<div style={{ display: "flex", justifyContent: "center" }}>
    <img
        src="https://s3.eu-central-1.amazonaws.com/corbado-cloud-staging-website-assets/debug_error_development_c80cdbdfaf.png"
        alt="Error pattern detail: Bitwarden Chrome extension bug trend over time"
        style={{ width: "75%" }}
    />
</div>

The error trend chart shows the
[Bitwarden](https://www.corbado.com/blog/passkey-analysis-bitwarden-developer-survey-2024) extension bug spiking
in November before declining again - correlated against total login volume (dashed line)
to distinguish real regressions from traffic fluctuations. The samples table at the bottom
surfaces the exact event type, error message, OS and browser for each occurrence, giving
engineers everything they need to reproduce and fix the issue.

### 5.3 Drilling into a single Process

From any error sample you can open the full process detail view - the individual
authentication process with its complete context, subprocesses and event timeline.

![Process detail view: conditional create errors on Windows Chrome with Bitwarden pattern match](https://s3.eu-central-1.amazonaws.com/corbado-cloud-staging-website-assets/process_details_error_16a7338711.png)

This example shows a user on Windows 10 with Chrome 145. The
[client capabilities](https://www.corbado.com/blog/webauthn-client-capabilities) section at the top confirms the
browser supports **ConditionalCreate** - a WebAuthn feature that silently creates a
passkey during a password login, upgrading the user without prompts. The process contains
four subprocesses: two login attempts and two append (passkey creation) attempts. The
second append failed with two errors - both flagged as `append-error-unexpected`. Drilling
into the events reveals one is a `clientPasskeyOperationErrorSilent` (the silent
[conditional create](https://www.corbado.com/blog/conditional-create-passkeys) failed with a non-cancel error)
and the other is a `cboApiNotAvailablePostAuthenticator` (API error after passkey
creation). Both errors are already automatically pattern-matched to "bug in Bitwarden
Chrome extension" - Corbado recognized the signature from prior occurrences. Despite the
errors in the second append, the fourth subprocess eventually completed successfully with
Bitwarden as the credential provider.

What makes this view powerful is the context available per error. You see the failure not
as an isolated log line but as a snapshot of the complete authentication state at the
moment it happened:

| Context                   | What it tells you                                                                              |
| ------------------------- | ---------------------------------------------------------------------------------------------- |
| **Existing passkeys**     | Which credentials the user already has, on which providers                                     |
| **Password managers**     | Installed credential providers (Bitwarden, 1Password, Google Password Manager) and their state |
| **Platform & OS version** | Windows / macOS / iOS / Android, exact version (from client hints, not just user agent)        |
| **Browser & version**     | Chrome, Safari, Firefox - including discrepancies between user agent and client hints          |
| **WebAuthn capabilities** | ConditionalCreate, ConditionalUI, Hybrid, PPKA, UVPA - what the device actually supports       |
| **Subprocess flow**       | Full sequence of login and append attempts within the process, with outcomes per step          |
| **Event timeline**        | Every event with timestamps, error types, messages and automatic pattern matches               |

This explains far more than log files ever could - you can immediately correlate a failure
with "this user has Bitwarden installed, no platform passkey, and the
[conditional create](https://www.corbado.com/blog/conditional-create-passkeys) was attempted after a successful
password login." That is the bridge between outcomes ("abort rate is up") and events
("[conditional create](https://www.corbado.com/blog/conditional-create-passkeys) fails on Windows Chrome 145 with
a Bitwarden extension bug") - without this level of drill-down, a spike in your abort
dashboard stays an abstract number; with it, you go from "something is broken" to a
reproducible bug report in minutes.

The natural next step is to zoom out from a single process into the full user history. The
user search view adds another layer of context:

| Context                                | What it tells you                                                                                                                                              |
| -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Client environments**                | Every distinct environment the user logged in from - different browsers, devices, incognito sessions. One user can have multiple client environments over time |
| **Client environment lifetime**        | Timeline showing when each environment was first and last seen, how the user's account behaves over weeks or months                                            |
| **Classification per environment**     | Each client environment gets its own passkey health classification and score - one environment can be `append-completed` while another is `missing-pk-support` |
| **Swim lanes per device**              | Side-by-side timeline of processes across environments, revealing parallel device use and whether a user switched devices after a failure                      |
| **Passkey and password manager state** | Which credentials exist on the account, which providers are active and when they were registered                                                               |

This level of context is where patterns like "authentication avoided by device change"
from section 4 become visible - but the user search workflow is a topic for a separate
article.

## 6. Conclusion

Native passkey deployments are harder than web. Your code is immutable once shipped,
[Android's](https://www.corbado.com/blog/how-to-enable-passkeys-android) fragmentation creates a 3-5x reliability
gap compared to iOS and the error surfaces differ across platforms, OS versions and
credential providers. Without visibility into what is actually breaking, teams discover
problems reactively - weeks after users have already churned.

The debug walkthrough in section 5 shows what changes with the right tooling: a device
health spike leads to an error classification, the classification leads to a trend chart,
the trend chart leads to a single process with full context - existing passkeys, password
managers, capabilities, the complete subprocess flow. You go from "abort rate is up" to a
reproducible bug report in minutes, not weeks.

The operational discipline that follows is straightforward: block device cohorts where
passkeys reliably fail, track error pattern trends to catch regressions before they impact
conversion and set different thresholds for iOS and Android based on each platform's
baseline. Treat passkey reliability as a continuous operational capability, not a one-time
feature launch.

For the complete web browser error reference (DOMException names, error messages by
browser engine), see [WebAuthn Errors](https://www.corbado.com/blog/webauthn-errors) in Production. For
platform-specific internal error code references, see GPS
[Passkey Error](https://www.corbado.com/blog/passkey-troubleshooting-solutions) Codes (Android) and Apple Passkey
Error Codes (iOS). For testing these patterns on real devices, see Testing Passkey Flows
in Native iOS & Android Apps. For setting up your analytics framework, see Passkey
Analytics.

## Frequently Asked Questions

### Why is my Android passkey abort rate so much higher than my iOS abort rate?

Android's open ecosystem creates massive behavioral variance: manufacturers customize
biometric stacks, Google Play Services versions differ across devices and budget hardware
has unreliable sensors. Google Pixel devices typically see 3-15% abort rates, premium
Samsung S-series 8-12%, budget Samsung A-series 20-40% and low-tier brands like ZTE or
vivo budget models 75-90%. iOS maintains consistency because Apple controls both hardware
and software, keeping most devices between 2-5%.

### How do I get more specific error information from Android passkey failures in a native app?

In native Android apps, Google Play Services exposes 50+ internal error codes via
`[50xxx]`-prefixed strings in the exception message before compressing them into 12
generic WebAuthn types. You can inspect the raw Credential Manager exception message for
this prefix pattern: for example, error 50162 identifies a Folsom sync key decryption
failure that Chrome would report only as a generic NOT_ALLOWED_ERROR. Parsing these raw
strings gives you root-cause specificity that is completely unavailable to web browsers.

### What is preferImmediatelyAvailableCredentials and why does it matter for passkey error handling?

`preferImmediatelyAvailableCredentials` is an option on both iOS
(ASAuthorizationController) and Android (Credential Manager) that returns a distinct error
immediately when no passkey exists on the device, rather than showing an empty system
sheet. This lets your app fall back cleanly to another authentication method and
accurately classify 'no credential available' separately from 'user cancelled' in your
analytics. Web browsers deliberately suppress this signal, making it a key diagnostic
advantage of native passkey implementation.

### How do I detect if users are silently avoiding passkeys in my native app without reporting errors?

Two behavioral patterns reveal silent passkey avoidance. Authentication loops, where the
app repeatedly invokes the authenticator and fails each time, can be detected server-side
by flagging 3 or more failed attempts from the same session within a short time window.
Device-change patterns after failed attempts, where a user switches from their native app
to a browser or a different device, indicate the user is actively working around a broken
passkey flow rather than reporting a problem.

### What abort rate thresholds should I set for passkey alerts in a native Android app?

Android abort rate thresholds must be set by manufacturer tier, not as a single number.
Google Pixel devices are healthy at 3-15%, premium Samsung S-series at 8-12%, budget
Samsung A-series at 20-40% and low-tier budget brands may exceed 75% even without a code
bug. Applying iOS thresholds, where 5% signals a likely problem, to Android will generate
constant false-positive alerts and cause you to miss real regressions on specific device
cohorts.
