---
url: 'https://www.corbado.com/ja/blog/webauthn-errors'
title: '本番環境におけるWebAuthnエラーの完全ガイド（2026年版）'
description: 'NotAllowedErrorなど、本番環境でよく発生するWebAuthnエラーの意味と、操作タイプ、タイミング、プラットフォームのコンテキストに基づいた分類方法について解説します。'
lang: 'ja'
author: 'Vincent Delitz'
date: '2026-07-03T07:08:54.561Z'
lastModified: '2026-07-03T07:10:43.687Z'
keywords: 'WebAuthnエラー, NotAllowedError, AbortError, SecurityError, WebAuthnトラブルシューティング, パスキーエラーの分類, 条件付き作成エラー, ASAuthorizationError, ASAuthorizationErrorコード, androidx.credentials, Credential Managerパスキーエラー, iOS'
category: 'WebAuthn Know-How'
---

# 本番環境におけるWebAuthnエラーの完全ガイド（2026年版）

## 1. はじめに

本番環境におけるWebAuthnエラーは、ブラウザが公開する`DOMException`の名称（`NotAllowedError`など）の数が少なく、複数の根本原因を表す可能性があるため、非常にわかりにくいものです。さらに、「エラー」の大部分（最適化された大規模な導入環境では多くの場合95%以上）は、実際には**想定内の動作**（ユーザーがオペレーティングシステムのパスキープロンプトを中止した）です。

> 重要: プライバシーの理由から、ブラウザはユーザーが意図的にキャンセルしたのか、パスキーが存在しなかったのかを区別しません。ただし、Webとネイティブプラットフォームの両方において、状況と十分なコンテキストがあれば、タイミングなどのシグナルを使用してこれらのケースを区別できる場合があります。

これらの名称の正規の定義を確認したい場合は、まず[MDN `DOMException`](https://developer.mozilla.org/en-US/docs/Web/API/DOMException)を参照してください。これらの例外につながるWebAuthn固有の条件（およびブラウザが強制する必要があるもの）については、[W3C Web Authentication仕様](https://www.w3.org/TR/webauthn-3/)を参照してください。

すべてのエラーを「バグ」として扱うと、誤った対応をとることになります。

- 正常なキャンセルによりエラーの指標が汚染される
- 大量の`NotAllowedError`に隠れた実際の不具合を見逃す
- ユーザーの回復を助けるどころか、混乱させるUIを提供してしまう

この記事では、以下の疑問に答えます。

- 実際のトラフィックにおいて、最も一般的なWebAuthnエラー名は通常何を意味するのか？
- `NotAllowedError`をどのようにして対応可能な分類（キャンセル、タイムアウト、可用性など）に明確化するか？
- なぜ同じエラーでも、操作（条件付きUIログイン、モーダルログイン、パスキー作成、条件付き作成）によって意味が異なるのか？
- 「失敗した」という事象を再現可能な問題にするために、最低限どのようなコンテキストをキャプチャすべきか？

## Key Facts

- `NotAllowedError`は**表面的なシグナル**であり、根本原因ではありません。コンテキストに応じて、キャンセル、タイムアウト、「ローカルの認証情報がない」、またはユーザーアクティベーションの欠如を意味する可能性があります。
- **操作タイプによって意味が変わります。** 条件付きUIログイン、モーダルログイン、手動によるパスキー作成、条件付き作成、および自動トリガーによる追加では、同じ`NotAllowedError`でも意味が異なります。
- **操作開始からのタイミング**は、最も過小評価されているシグナルです。即時（1秒未満）、ユーザーキャンセル（1〜15秒）、タイムアウト（30秒以上）は、根本的に異なるカテゴリです。
- `AbortError`は通常、ライフサイクルまたは並行処理の問題（ナビゲーション、再レンダリング、複数の進行中のリクエスト）です。
- `SecurityError`はほぼ常に設定やコンテキストの問題であり、成熟した本番環境での導入では稀です。
- 「ブラウザのエラー名」と「サーバーの検証拒否」は異なるレイヤーです。サーバーの拒否は、一般的なクライアントの失敗としてではなく、明示的なコードとして追跡します。
- **生のエラー名だけでは対応できません。** エラーを実際に修正可能な分類に分けることができるように、常に`error.name`と一緒に操作タイプ、タイミング、プラットフォームのコンテキストをキャプチャしてください。
- **「環境」は単なるブラウザとOSの組み合わせ以上のもので**す。エラーを真に理解するには、OSのバージョン、クライアント（ブラウザ/アプリのバージョン）、Authenticatorの設定（例：iCloud/GPMのステータス）、および特定のハードウェアモデルの完全な組み合わせを追跡する必要があります。
- **ログインの失敗はP1、作成の失敗はP2です。** 作成エラー（P2）はユーザーの離脱により量が多くなることが多いのに対し、ログインエラー（P1）はアクセスをブロックするため、即時のアラートが必要です。

## 2. 本番環境向けチートシート

デバッグのブロックを解除するための簡単なマッピングが必要な場合は、まずこの表から始めてください。これは、チームがダッシュボードやサポートチケットで実際に目にする情報に偏っています。

| **`error.name`**    | **本番環境で通常意味するもの**                                                                                                                                           | **確認事項**                                                                                                           | **最初のアクション（UX + エンジニアリング）**                                                                                      |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `NotAllowedError`   | ユーザーがシートを閉じた、タイムアウトした、または可用性の不一致が1つの分類にまとめられたもの。本番環境で最も多いエラー分類です。                                        | エラーまでの時間、QR/ハイブリッドUIが表示されたかどうか、セレモニーが実際のユーザーアクションから開始されたかどうか    | 想定内として扱う：UIを復元し、フォールバックを表示する                                                                           |
| `AbortError`        | アプリ（またはブラウザ）がセレモニーを中止した                                                                                                                           | セレモニー中のナビゲーション/再レンダリング、同時並行のWebAuthn呼び出し、`AbortController.abort()`                     | 進行中のリクエストを1つに制限する、ルート変更を防ぐ、中止を通常の制御フローとして処理する                                        |
| `SecurityError`     | コンテキスト/ポリシーが許可されていない                                                                                                                                  | オリジンとRP ID戦略、iframe/埋め込み、HTTPS、機能ポリシー                                                              | RP ID/オリジンの設定を修正する、埋め込みポリシーを検証する、セキュアコンテキストを確保する                                       |
| `InvalidStateError` | 状態の不一致（多くの場合、重複した登録）                                                                                                                                 | 登録とログイン、`excludeCredentials`、Authenticator上の既存の認証情報                                                  | 「すでに登録済み」として扱う、UXパスを調整する、オプション生成を修正する                                                         |
| `ConstraintError`   | 要件を満たせない                                                                                                                                                         | `authenticatorAttachment`、`userVerification`、常駐キーの要件                                                          | 制約を緩和するか、代替パス/フォールバックを提供する。例：Androidで画面ロックが設定されていない                                   |
| `DataError`         | 入力が不正/矛盾している                                                                                                                                                  | base64urlエンコーディング、ID/チャレンジ/ユーザーハンドルの形式                                                        | エンコーディング/シリアライズを修正する、オプション生成で検証を追加する                                                          |
| `NotSupportedError` | プラットフォーム/ブラウザが要求された機能をサポートしていない                                                                                                              | OS/ブラウザのバージョン、機能検出の前提                                                                                | すぐにフォールバックする、セグメントを記録する、サポートされていない環境ではパスキーのCTAを表示しない                            |
| `UnknownError`      | プラットフォーム/Authenticatorが一般的な方法で失敗した                                                                                                                   | OSアップデート後の急増、デバイスビルド、Credential Managerプロバイダーの問題                                           | 再試行しやすいUXにする、ビルド番号をキャプチャする、セグメントの急増を調査する                                                   |

見落としがちなこととして、同じ`error.name`でも**操作タイプ**によって意味が全く異なる場合があります。以下のセクションを読む際は、操作のコンテキストに留意してください。実際には、パスキーの作成（登録）エラーはログインエラーを大きく上回るのが通常です。上の表は両方に適用されますが、ボリュームの大部分は作成時に発生します。

次に、チームが最も頻繁に目にし、最も誤解しやすい`NotAllowedError`について深く掘り下げます。

## 3. NotAllowedError: 操作がタイムアウトしたか、許可されなかった

`NotAllowedError`は「パスキーが失敗した」ように見えることが多いですが、通常はプラットフォームが「ユーザーがOSのUIを完了しなかった」と伝えているものです。重要なのは、それを対応可能な分類に分けることです。

**ブラウザのコンソールに表示される内容:**

| **ソース**     | **エラーメッセージ**                                                                                                                                                 |
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Chrome, Edge   | `NotAllowedError: The operation either timed out or was not allowed. See: https://www.w3.org/TR/webauthn-2/#sctn-privacy-considerations-client.`                     |
| Safari, WebKit | `NotAllowedError: The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.`                 |
| Safari, WebKit | `NotAllowedError: This request has been cancelled by the user.`                                                                                                      |
| Chrome, Edge   | `NotAllowedError: The operation is not allowed at this time because the page does not have focus.`                                                                   |
| Safari, WebKit | `NotAllowedError: The document is not focused.`                                                                                                                      |
| Firefox        | `NotAllowedError: Operation failed.`                                                                                                                                 |

これらはすべて`error.name === "NotAllowedError"`として表面化します。`error.message`はブラウザエンジンや根本原因によって異なりますが、結果は同じで、セレモニーが完了しなかったということです。

これは**ログインとパスキーの作成**の両方に適用されます。ログイン中（条件付きUI、allowListの有無にかかわらないモーダル）の`NotAllowedError`は、通常、ユーザーがセレモニーを完了しなかったことを意味します。パスキーの作成中にも、異なる理由で同じエラーが表面化します。ユーザーが作成ダイアログを閉じた（条件付き作成が機能しなかった）、または自動トリガーによる追加中にページがフォーカスを失った場合などです。操作タイプによってエラーの意味と対処法が変わります。

**タイミングは過小評価されがちなシグナルです。** クリックから1秒未満のエラーは、通常、即時の拒否（環境が実行できない、ドキュメントにフォーカスがない、機能がない）です。数秒後のエラーはユーザーのキャンセル（ダイアログを見て進めないことを決定した）です。30秒以上経過した後のエラーはタイムアウトです。ネイティブプラットフォームではタイミングが特に重要で、Authenticatorのラウンドトリップ、生体認証プロンプト、Credential Managerのハンドオフにはそれぞれ特徴的な所要時間があり、「機能しなかった」のか「ユーザーが離れた」のかを区別するのに役立ちます。それでも、パスキーが存在したかどうかを簡単に区別することはできません。

### 3.1 コンテキストによる明確化

完璧なシグナルは必要ありません。すべての`NotAllowedError`を同じように扱わないようにするための十分なコンテキストが必要です。iOS/Safariについては、独自の制約（以前のバージョンでのユーザーアクティベーション要件）があるため以下で特に注意を向けますが、生のエラーボリュームで見ると、WindowsとChromiumブラウザは他のどのプラットフォームよりも多くの`NotAllowedError`を生成することがよくあります。これらのシグナルを活用することで、多くの場合80%の対応が可能になります。

| **シグナル**                                                   | **可能性の高い意味**                                                                                                                                                                                                                                                                           | **次のアクション**                                                                                                                                                                   |
| -------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 即時の失敗（1秒未満）                                          | 環境の拒否：機能がない、ドキュメントにフォーカスがない、条件付き作成サーフェスが利用できない                                                                                                                                                                                                 | 機能検出を確認する、ドキュメントにフォーカスがあるか確認する、このプラットフォームで操作がサポートされているか検証する                                                               |
| 早いキャンセル（1〜3秒）                                       | 予期せぬプロンプト / コンテキストがない                                                                                                                                                                                                                                                        | プロンプトのタイミングを変更する、キャンセル後にクールダウンを追加する                                                                                                               |
| ユーザーの長さのキャンセル（3〜15秒）                          | ユーザーがダイアログを見て進めないことを選んだ                                                                                                                                                                                                                                                 | 想定内のUX：UIを復元し、フォールバックを表示する                                                                                                                                     |
| タイムアウト（30秒以上）                                       | ユーザーのアクションなしにセレモニーがタイムアウトした                                                                                                                                                                                                                                         | 「完了しなかった」として分類する、プロンプトが気づかれていたかどうか検討する                                                                                                         |
| 失敗の前にQR/ハイブリッドUIが表示される                        | このデバイスで利用可能なローカルの認証情報がない。注：QRコードの決定が行われる前に確実に検出するには、現在のデバイスで使用可能な認証情報が存在するかどうかを把握する[パスキーインテリジェンス](https://docs.corbado.com/corbado-connect/features/passkey-intelligence)レイヤーが必要です。 | パスキーの提案を制限する、「スマートフォンを使用する」を明示する、予期せぬQRを減らす                                                                                                 |
| iOS/Safariに集中しており、クリック/タップなしでトリガーされる  | ユーザーアクティベーションの欠如                                                                                                                                                                                                                                                               | 実際のユーザーのジェスチャーからセレモニーを開始する                                                                                                                                 |
| 条件付き作成または自動トリガーによる追加中                     | 自動入力が利用できない、認証情報がすでに存在する、またはページがフォーカスを失った。条件付き作成エラーは、機能がリリースされると突然大量に発生する可能性があり、一晩で最大のエラーソースの1つになります。                                                                                    | 条件付き作成を確認する、ドキュメントの表示状態を確認する、呼び出しを試行する前に`getClientCapabilities()`を使用して`conditionalCreate`のサポートを検証する                           |

これも`NotAllowedError`をユーザーに表示すべきではない理由の1つです。これはユーザーが対応できるメッセージではありません。

コンテキストの分類は、成功率が最も大きく分かれる部分でもあります。[Corbadoのパスキーベンチマーク2026年 パスキー認証成功率分析](https://www.corbado.com/passkey-benchmark-2026/passkey-authentication-success-rate)によると、大規模なB2C展開において、未知のデバイスでの識別子ファーストフローの完了率が55〜95%であるのに対し、既知のデバイスの再訪問では95〜99%となっています。iOS Webの識別子ファーストは完了率が85〜95%（CDA 0〜5%）、Android Webは70〜85%（CDA 5〜10%）、macOS Webは70〜85%（CDA 10〜15%）に着地しますが、Windows Webの識別子ファースト完了率は45〜60%で、CDAの割合はWindows 11で55〜65%、Windows 10で40〜55%です。既知のデバイスと未知のデバイスのコンテキストを分けずに`NotAllowedError`のボリュームを読み取ると、根本的に異なる2つの成功の状況を混同することになります。

本番環境で重要なニュアンスが1つあります。一部のユーザーエージェントはタイムアウトを`TimeoutError`として表示する場合がありますが、多くのチームはダッシュボードでタイムアウトが`NotAllowedError`に統合されているのを目にします。いずれにせよ、タイムアウトは「セレモニーが完了しなかった」として扱い、タイミングとコンテキストを使用して分類してください。

### 3.2 UXの処理：キャンセルを通常の終了とする

OSのシートが閉じられたりタイムアウトしたりした場合、UIは即座に回復し、適切に反応する必要があります。実用的なルールのセットは以下の通りです。

- ログインUIを復元する（動作し続けるスピナーは使用しない）
- 識別子の状態を維持する（再入力を強制しない）
- 同じ画面に視認可能なフォールバックを表示する
- 想定される結果に対して恐ろしいバナーを表示しない

基本を超えた対応：

- 最初のキャンセルの段階では通常のものとして扱い、落ち着いた説明とともに再試行を提供します。2回目のキャンセルの後でのみ、フォールバックオプションを提案します。
- 驚いたユーザーが2度目のチャンスを得られるように、クールダウンの前に最大3回の作成プロンプトを許可します。
- 類似の条件を比較できるように、作成とログインでエラーの集計を分けます。
- 実際にどこに摩擦が存在するかを発見できるように、OS、ブラウザ、デバイスごとにエラー率をセグメント化します。

「キャンセル」が本当に多い場合、次のステップは、プロンプトのタイミング、予期せぬQR、可用性の低さなど、その背後にある根本原因を修正することです。

### 3.3 NotAllowedErrorを減らすエンジニアリングの修正

指標をすばやく改善できる変更から始めます。

- プロンプトのタイミングとユーザーアクティベーション（特にiOS/Safari上）を修正します。
- 予期せぬQR/ハイブリッドを減らします。
- パスキーが成功する可能性が高い場合にのみ表示されるように、提案を制限します。
- ローカルの認証情報が存在しない場合にプロンプトを表示しないパターンを使用します。
- **ネットワークの不安定性への対処:** 検証中のネットワークエラーは、クライアント側の一般的なエラーとして現れることがよくあります。セレモニー中にユーザーの接続が切断された場合にエラーデータを失わないように、テレメトリログのオフラインキューを実装してください。
- **検出APIでセレモニーを制限する:** 環境の障害によって`NotAllowedError`の分類が膨れ上がらないようにします。最も基本的な制限として`isUVPAA()`から始め、次に`getClientCapabilities()`を使用してより詳細なチェック（条件付き作成、条件付き取得、ハイブリッドトランスポート、プラットフォームAuthenticator）を行います。検出APIはOSのアップデートによって壊れる可能性があることに注意してください。iOS 26.2では、パスキーが正常に機能するにもかかわらず、すべてのWKWebViewベースのブラウザで`isUVPAA()`が`false`を返すというWebKitのバグが含まれており、iOSユーザーの10〜25%で`NotAllowedError`が急増しました。

### 3.4 進化するエラー名についての注意

エラー名は常に変化しています。より詳細なWebAuthnエラーを追加する（たとえば、「利用可能な認証情報がない」ことと「ユーザーがキャンセルした」ことを分けるなど）継続的な提案があります。2026年2月現在、これはどのブラウザにも実装されていないため、コンテキストとタイミングに基づいて独自の理由の分類を構築する価値は依然としてあります。この作業を追跡したい場合は、[WebAuthnのIssue #2062](https://github.com/w3c/webauthn/issues/2062)と、["New Error Codes" explainer](https://github.com/w3c/webauthn/wiki/Explainer%3A-New-Error-Codes-%282024-Edition%29)を参照してください。

残りのエラー名は頻度が低いですが、出現した際に理解しておく価値があります。

## 4. AbortError: 操作が中止された

`AbortError`は`NotAllowedError`に比べて量は少ないですが、出現した場合の診断価値は高いです。これは通常、アプリがリクエストを無効にした（ナビゲーションが発生した、状態が変わった、または2番目のリクエストが開始された）ためにセレモニーが終了しなかったことを意味します。

**ブラウザのコンソールに表示される内容:**

| **ソース**     | **エラーメッセージ**                                      |
| -------------- | --------------------------------------------------------- |
| Chrome, Edge   | `AbortError: The operation was aborted.`                  |
| Chrome, Edge   | `AbortError: Aborted by AbortSignal.`                     |
| Firefox        | `AbortError: signal is aborted without reason`            |
| Firefox        | `AbortError: Operation timed out.`                        |
| Safari, WebKit | `AbortError: The user aborted a request.`                 |
| Chrome         | `AbortError: CredentialContainer request is not allowed.` |

本番環境での一般的な原因には以下が含まれます。

- 複数の同時並行のWebAuthn呼び出し（2つのプロンプトが競合している）
- 進行中のセレモニー中のルート変更/再レンダリング
- 再試行または状態のクリーンアップ中の`AbortController.abort()`の呼び出し

これを修正するには、セレモニーを「クリティカルセクション」にすることに焦点を当てます。

- 進行中のリクエストを一度に1つだけ許可する
- セレモニー中のナビゲーションをブロックする（またはきれいにキャンセルしてUIを復元する）
- 中止を想定内の制御フローとして扱う：再試行ボタンとフォールバック手段を表示する

埋め込みサーフェスやマルチドメインアプリで`AbortError`が集中しているのを見つけた場合、次に確認すべき分類は`SecurityError`です。

## 5. SecurityError: TLS証明書エラーのあるサイトではWebAuthnはサポートされていません

`SecurityError`は、ブラウザが「このコンテキストでは要求された操作が許可されていません」と伝えているものです。実際には、ユーザーの行動ではなく設定に問題があることがほとんどです。成熟した本番環境での導入では、これらの問題は通常統合テスト中に捕捉されるため、`SecurityError`はまれです。本番環境で発生した場合、通常は適切なWebAuthn構成なしに新しいドメイン、埋め込みコンテキスト、またはデプロイメントターゲットが追加されたことを意味します。

一般的な原因には以下が含まれます。

- RP ID / オリジンの不一致（マルチドメイン設定）
- クロスオリジン埋め込みの制限（iframe）
- セキュアでないコンテキスト（HTTPSではない）、またはブロックされた権限/ポリシー
- `.well-known/webauthn` または `.well-known/assetlinks.json`の設定ミス、欠落、または一時的な利用不可。ブラウザがこれらのファイルをフェッチする重要な時間枠でのネットワーク問題は、失敗の原因となります。よくある盲点：ホームページがメンテナンスでダウンしている場合、well-knownファイルもオフラインになり、それらに依存するすべてのRelying Partyでパスキーのセレモニーが中断されます。

**ブラウザのコンソールに表示される内容:**

| **ソース**      | **エラーメッセージ**                                                                                                             |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| Chrome, Edge    | `SecurityError: WebAuthn is not supported on sites with TLS certificate errors.`                                                 |
| 任意のブラウザ  | `SecurityError: The relying party ID is not a registrable domain suffix of, nor equal to the current origin's effective domain.` |
| Chrome (iframe) | `SecurityError: The 'publickey-credentials-create' feature is not enabled in this document.`                                     |

本番環境では、`SecurityError`はまれです。これらはほぼ常に統合テスト中に捕捉されます。発生した場合、TLS証明書エラーが最も一般的な残存エラーです。

最も速いデバッグループは以下の通りです。

- 使用したオリジンとRP IDの入力を記録する
- 同じコンテキスト（トップレベル対iframe、本番ドメイン対ステージング）で再現する
- ログインを埋め込む場合、権限ポリシーが設定されていることを確認する（たとえば、`publickey-credentials-create` / `publickey-credentials-get`）：[MDN Permissions-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Permissions-Policy/publickey-credentials-create)
- ドメイン戦略を検証する
- iframeを使用している場合は、機能ポリシーを検証する

`SecurityError`が処理された後、次に真剣に扱うべき分類は、多くの場合実装のバグを示すエラー群である`InvalidStateError`、`ConstraintError`、および`DataError`です。

## 6. InvalidStateError, ConstraintError, DataError: 実装のバグとして扱う

これらのエラーは、成熟したパスキーの実装ではまれなはずです。これらが表示された場合、通常はセグメントのオプション生成が間違っているか、フローが間違った状態にあることを示しています。

### 6.1 InvalidStateError: "Relying Partyにすでに登録されている認証情報"

**ブラウザのコンソールに表示される内容:**

| **ソース**     | **エラーメッセージ**                                                                                                                                 |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| Safari, WebKit | `InvalidStateError: The user attempted to register an authenticator that contains one of the credentials already registered with the relying party.` |
| Chrome, Edge   | `InvalidStateError: At least one credential matches an entry of the excludeCredentials list in the platform attached authenticator.`                 |
| Chrome, Edge   | `InvalidStateError: A request is already pending.`                                                                                                   |
| Firefox        | `InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable`                                                       |

典型的な意味：

- 登録：すでに存在する認証情報の作成を試みた（重複）
- ログイン：あまり一般的ではありません。プラットフォームに対してフロー/状態が矛盾していることを意味することが多いです。

実用的な処理：

- 手動での登録でこれが発生した場合は、「すでに登録済み」として扱い、それに応じてルーティングします。
- Authenticatorが重複を検出できるように、`excludeCredentials`にユーザーのすべての既存の認証情報IDがリストされていることを確認します。
- 条件付き作成中、`InvalidStateError`は想定内であり、無視すべきです。これは、プロバイダーにパスキーがすでに存在することを意味します。条件付き作成中の`NotAllowedError`および`AbortError`にも同じことが当てはまります（[Chromeでの条件付き作成](https://developer.chrome.com/docs/identity/webauthn-conditional-create)を参照）。

### 6.2 ConstraintError

典型的な意味：Authenticatorが要求された制約を満たすことができません。

一般的なトリガー：

- デバイスの画面ロックがない（特にAndroid）：プラットフォームは生体認証またはPINによる検証を要求していますが、デバイスにロック画面が設定されていません。
- 厳格すぎる`authenticatorAttachment`または常駐キーの前提
- `userVerification`が利用できないセグメントでのハードな要件

修正：制約を緩和する（許容される場合）か、代替パスを提供します。画面ロックがない場合、何も言わずに失敗するのではなく、この状態を検出してユーザーをガイドすることを検討してください（Android）。

### 6.3 DataError

典型的な意味：入力が不正であるか、矛盾しています。

一般的なトリガー：

- エンコーディングのミス（base64対base64url）
- 無効な認証情報ID / チャレンジのフォーマット

修正：WebAuthnオプションを生成する境界で入力を検証し、正規化します。実際には、成熟した本番システムでは`DataError`は実質的に存在しません。オプション生成がテストされていれば、ダッシュボードでこれを見ることはありません。

これらのエラーがコントロール下にある場合、次の疑問はカバレッジです。環境が期待通りの方法でWebAuthnを実行できないためにユーザーが失敗しているのでしょうか？

## 7. NotSupportedError: ユーザーエージェントは公開鍵認証情報をサポートしていません

`NotSupportedError`はカバレッジのシグナルであり、信頼性のシグナルではありません。通常、セグメントが要求されたことを実行できない（OS/ブラウザが古すぎる、機能が欠けている、機能が有効になっていない）ことを意味します。

**ブラウザのコンソールに表示される内容:**

| **ソース**            | **エラーメッセージ**                                                                           |
| --------------------- | ---------------------------------------------------------------------------------------------- |
| Chrome, Edge          | `NotSupportedError: The user agent does not support public key credentials.`                   |
| Firefox               | `NotSupportedError: Resident credentials or empty 'allowCredentials' lists are not supported.` |
| Chrome, Edge, Firefox | `TypeError: PublicKeyCredential.parseCreationOptionsFromJSON is not a function`                |
| Chrome, Edge, Firefox | `TypeError: PublicKeyCredential.parseRequestOptionsFromJSON is not a function`                 |
| Chrome, Edge, Firefox | `TypeError: credential.toJSON is not a function`                                               |
| Safari                | `TypeError: Can only call PublicKeyCredential.toJSON on instances of PublicKeyCredential`      |

最初の2つは本物の`NotSupportedError`のDOMExceptionsです。`TypeError`のエントリは技術的には異なる例外タイプですが、同じクラスの問題、つまりブラウザや環境が要求されたものをサポートしていないことを表しています。JSONシリアライゼーションの`TypeError`ファミリーは、実際には`NotSupportedError` DOMException自体よりもはるかに一般的です（下記参照）。

一般的な原因には以下が含まれます。

- 基本的なWebAuthnをサポートしていない古いブラウザ/OSのバージョン
- そのプラットフォームで利用できないWebAuthnの機能を要求している
- サポートされていないデバイスでプラットフォーム固有のフローを試みている

**JSONシリアライゼーションファミリーは、本番環境における`NotSupportedError`クラスの障害の最大の原因です。** 技術的にはこれらは`DOMException`ではなく`TypeError`（メソッドの欠落）として表面化しますが、ここで遭遇することになります。2つの明確な根本原因があります。

1. **ブラウザがWebAuthn JSONシリアライゼーションメソッドをサポートしていない。** ブラウザに`navigator.credentials`はありますが、`PublicKeyCredential.parseCreationOptionsFromJSON` / `parseRequestOptionsFromJSON`はありません。これは、このエラーファミリーの約90%を占め、古いバージョンのSafariとChromeに集中しています。クライアントライブラリがフォールバックなしにこれらのメソッドに依存している場合、これにより大量のエラーが発生します。
2. **パスワードマネージャーの拡張機能が`.toJSON()`を壊す。** Bitwarden、LastPass、または1Passwordなどの拡張機能は、セレモニーを傍受し、認証情報のように見えるが実際の`PublicKeyCredential`インスタンスではないオブジェクトを返す可能性があります。これに対して`.toJSON()`を呼び出すと、エラーがスローされるか、undefinedが返されるか、オブジェクトが完全に`null`になります。これはこのファミリーの約10%を占めますが、エラーメッセージがブラウザによって異なる（Safari：「Can only call on instances of PublicKeyCredential」、Firefox：「does not implement interface PublicKeyCredential」）ため、デバッグが特に困難です。

対処は直接的かつ迅速に行う必要があります。

- すぐにフォールバックのパスワード/OTPにフォールバックする
- カバレッジのギャップを定量化できるようにセグメントを記録する
- 一貫して失敗するセグメントにパスキーのCTAを表示しないようにする

カバレッジが良好に見えても、特定のセグメントで障害が発生する場合は、`UnknownError`として表面化したプラットフォームレイヤーの問題に対処している可能性があります。

## 8. UnknownError: Credential Managerとの通信中に不明なエラーが発生しました

`UnknownError`は、他のカテゴリにきれいにマッピングされないAuthenticator/OSの障害のキャッチオールです。一時的なものであることが多いですが、OSのアップデート後に急増することもあります。

**ブラウザのコンソールに表示される内容:**

| **ソース**         | **エラーメッセージ**                                                                                                                    |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------- |
| Chrome (Android)   | `UnknownError: An unknown error occurred while talking to the credential manager.`                                                      |
| 任意のブラウザ     | `UnknownError: The operation failed for an unknown transient reason.`                                                                   |
| 任意のブラウザ     | `UnknownError: Either the device has received unexpected request data, or the device has been reconfigured since the request was made.` |
| 任意のブラウザ     | `UnknownError: Something went wrong.`                                                                                                   |
| Chrome (LastPass)  | `TypeError: Cannot use 'in' operator to search for 'type' in null`                                                                      |
| Safari (LastPass)  | `TypeError: null is not an Object. (evaluating 'key in input')`                                                                         |
| Chrome (Bitwarden) | `FallbackRequested`                                                                                                                     |

実用的な処理：

- 再試行しやすいUXを使用する（「ユーザーを責めない」）
- 可能な限り、OS/ビルド番号とCredential Manager/プロバイダーのコンテキストをキャプチャする
- OSのアップデート後のセグメント固有の急増に注意する

どの`DOMException`カテゴリにもきれいに当てはまらないニッチなエラーソースの1つに、**パスワードマネージャーのブラウザ拡張機能**（Bitwarden、LastPass、1Passwordなど）があります。これらはWebAuthn API呼び出しを傍受し、非標準のレスポンスを返す可能性があります。ユーザーのキャンセルと比較すると量は少ないですが、特定のユーザーセグメントに一貫して影響を与え、症状（返された認証情報オブジェクトのメソッドの欠落、予期しないエラータイプ、または文書化されたWebAuthnエラーと一致しない不正なレスポンス）がわかりにくいため、追跡する価値があります。これらは多くの場合、`UnknownError`として、または分類されない例外として表面化します。OSレベルでの説明がつかない特定のブラウザに集中したエラーの急増が見られる場合は、Credential Manager拡張機能が関与していないか確認してください。

これまではWebブラウザのエラー名を取り上げてきました。しかし、ネイティブアプリも提供している場合、エラーの状況は異なり、ある意味で大幅に改善されています。

## 9. ネイティブアプリ（iOSおよびAndroid）について

上記のすべてはWebブラウザに関するものです。ネイティブアプリ（ASAuthorizationフレームワークを使用するiOS、Credential Managerを使用するAndroid）は、同じ基本的なエラーカテゴリを共有していますが、重要な点で異なります。

1. **「認証情報がない」は明確なシグナルです。** Webでは、プライバシー保護のためにブラウザが「利用可能な認証情報がない」ことと「ユーザーがキャンセルした」ことを同じ`NotAllowedError`に統合します。ネイティブでは、iOS（`ASAuthorizationController`）で`preferImmediatelyAvailableCredentials`を使用するか、Android（`GetCredentialRequest`）で`setPreferImmediatelyAvailableCredentials(true)`を使用すると、デバイスにすでに存在する認証情報のみを提示し、存在しない場合はすぐに失敗するようにOSに指示します。これにより、Webでは提供できないクリーンな「認証情報がない」という結果が得られます。

2. **Credential Providerのステータスが可視化されています。** ネイティブプラットフォームでは、特定の状況下で、Credential Provider（Google Password Manager、iCloudキーチェーン、1Passwordなど）がインストール、設定、またはデフォルトとして設定されていないことを把握し、それに対応することができます。Web上では、この情報は不透明な`NotAllowedError`メッセージの後ろに隠されています。

3. **エラーメッセージがより具体的です。** ユーザーがアプリをインストールしており、それによってRelying Partyとの信頼関係が確立されているため、OSはより詳細な診断情報を表示します。アプリがすでにデバイス上にある場合、Webブラウザを曖昧にするプライバシーの考慮事項は同じようには適用されません。iOSはユーザーのデバイス言語にローカライズされたメッセージを返します。Androidは原因チェーンを含む構造化されたエラータイプを返します。これによりデバッグが容易になりますが、エラー処理ではローカリゼーションとプラットフォーム固有のエラーフォーマットを考慮する必要があります。

### 9.1 iOS (ASAuthorizationフレームワーク)

iOSは、ASAuthorizationフレームワークを通じてパスキーエラーを表示します。すべてのエラーは、`authorizationController(controller:didCompleteWithError:)` デリゲートコールバックに`NSError`オブジェクトとして到達します。

**メッセージ文字列ではなく、ドメイン + コードで分類します。** 主なエラードメインは`com.apple.AuthenticationServices.AuthorizationError`（`ASAuthorizationError.errorDomain`）です。`let nsError = error as NSError`でエラーをキャストし、`.domain`と`.code`で一致させます。本番環境で`.localizedDescription`と照合しないでください。Appleのメッセージは30以上の言語にローカライズされており、OSバージョン間で変更される可能性があります。以下にリストされているメッセージ文字列は、ログ内のエラーを認識するのに役立ちますが、分類の基準ではありません。

**公開されているASAuthorizationErrorコード:**

| **コード** | **名前**                                | **導入** | **意味**                                                                                                                                                                                                                                                                                          |
| ---------- | --------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1000       | `Unknown`                               | iOS 13   | 本番環境には表示されないはずです。一般的なキャッチオール。                                                                                                                                                                                                                                        |
| 1001       | `Canceled`                              | iOS 13   | ユーザーがパスキーのシートを閉じました。全体で最も多いエラーであり、`NotAllowedError`に相当します。空の`userInfo`と根本的なエラーのないクリーンなシグナル。                                                                                                                                       |
| 1002       | `InvalidResponse`                       | iOS 13   | フレームワークレベルの破損。実際にはまれです。                                                                                                                                                                                                                                                    |
| 1003       | `NotHandled`                            | iOS 13   | リクエストを処理したプロバイダーはありません。エンタイトルメントとCredential Providerの設定を確認してください。                                                                                                                                                                                   |
| 1004       | `Failed`                                | iOS 13   | 一般的な失敗。`localizedDescription`に実際の理由（例：「識別子Xを持つアプリケーションはドメインYに関連付けられていません」）が含まれています。`userInfo`には`FailureReason`文字列が含まれる場合がありますが、`NSUnderlyingErrorKey`は常に存在するとは限りません。ドメインの関連付けの失敗ではnilが返されます。 |
| 1005       | `NotInteractive`                        | iOS 15   | `preferImmediatelyAvailableCredentials`を使用した場合に利用可能な認証情報がありません。これはクリーンな「見つからない」シグナルであり、「このデバイスにパスキーは存在しない」というiOS版です。UIは表示されませんでした。                                                                          |
| 1006       | `MatchedExcludedCredential`             | iOS 18   | このデバイスのこのRPにはパスキーがすでに存在します。重複検出のためのクリーンなシグナルです。空の`userInfo`で`NSUnderlyingErrorKey`はありません。文字列のマッチングなしで分類が機能します。                                                                                                        |
| 1007       | `CredentialImport`                      | iOS 18.2 | 認証情報のインポートに失敗しました。                                                                                                                                                                                                                                                              |
| 1008       | `CredentialExport`                      | iOS 18.2 | 認証情報のエクスポートに失敗しました。                                                                                                                                                                                                                                                              |
| 1009       | `PreferSignInWithApple`                 | iOS 26   | ユーザーがパスキーよりも「Appleでサインイン」を優先しています。iOS 26の新機能。                                                                                                                                                                                                                   |
| 1010       | `DeviceNotConfiguredForPasskeyCreation` | iOS 26   | デバイスにパスコードまたはiCloudキーチェーンの設定がありません。iOS 26の既知のシミュレーターのバグ：設定が正しい場合でも1010を返します（物理デバイスでは再現しません）。                                                                                                                            |

本番環境で最も重要な違いは、**iOS 18以降、重複した認証情報は独自のエラーコード1006（`MatchedExcludedCredential`）を返す**ということです。iOS 17以前では、重複した認証情報はコード1004（`Failed`）の中に隠されていました。iOS 18+では、区別は構造的（異なるエラーコード）であり、テキストによるものではありません。

**一般的なランタイムエラー（ログレベルの参照）:**

これらのメッセージは、特定の失敗シナリオの`localizedDescription`または`userInfo`に表示されます。ログの検索とデバッグに使用し、プログラムによる分類には使用しないでください。

| **メッセージ（英語ロケール）**                                                  | **根本的なコード** | **メモ**                                                                                                                                                         |
| ------------------------------------------------------------------------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Application with identifier <TeamID.BundleID> is not associated with domain X` | 1004 (`Failed`)    | アプリのAssociated DomainsエンタイトルメントがRelying Partyと一致していません。サーバー上の`apple-app-site-association`ファイルを修正してください。                |
| `Couldn't communicate with a helper application.`                               | 1004 (`Failed`)    | Credential Provider拡張機能が応答しませんでした。一時的なものなので、再試行が適切です。                                                                          |
| `Request already in progress for specified application identifier.`             | 1004 (`Failed`)    | 保留中のASAuthorizationリクエストがある状態で重複して発行されました。アプリでの競合状態。                                                                        |
| `Stolen Device Protection is enabled and biometry is required.`                 | 1004 (`Failed`)    | iOS 17以降の「盗難デバイスの保護」により、不慣れな場所での生体認証がブロックされます。開発者が対応できるものではありませんが、ユーザーに表示する価値はあります。 |
| `(AuthenticationServicesCore.ASCABLEClient.ClientError error 2.)`               | 別ドメイン         | クロスデバイス認証（ハイブリッド/CABLE）のBluetoothハンドシェイクが失敗しました。                                                                                |
| `(AuthenticationServicesCore.ASCABLEClient.ClientError error 3.)`               | 別ドメイン         | クロスデバイス認証のBluetooth接続が失敗しました。                                                                                                                |

**ローカライズされた「認証情報がありません」というメッセージ（コード1005）:**

`preferImmediatelyAvailableCredentials`が設定されており、パスキーが存在しない場合、iOSはユーザーのデバイス言語にローカライズされたメッセージとともにコード1005（`NotInteractive`）を返します。これはネイティブアプリに固有のものであり、Webブラウザがこのシグナルを公開することはありません。メッセージは常に`The operation couldn't be completed.`（操作を完了できませんでした）で始まり、その後にローカライズされたテキストが続きます。

| **言語**            | **メッセージ**                                                                       |
| ------------------- | ------------------------------------------------------------------------------------ |
| 簡体字中国語        | `没有可用于登录的凭证。`                                                             |
| ベトナム語          | `Không có sẵn thông tin để đăng nhập.`                                               |
| アラビア語          | `لا تتوفر بيانات اعتماد لتسجيل الدخول.`                                              |
| スペイン語          | `No hay ninguna credencial disponible para iniciar sesión.`                          |
| 繁体字中国語        | `沒有可用於登入的憑證。`                                                             |
| 韓国語              | `로그인을 위한 자격 증명이 없습니다.`                                                |
| フランス語（カナダ）| `Aucun identifiant disponible pour la connexion.`                                    |
| ポルトガル語（ブラジル）| `Nenhuma credencial disponível para login.`                                      |
| フランス語（フランス）| `Aucune information d'identification n'est disponible pour procéder à la connexion.`|
| タイ語              | `ไม่มีข้อมูลประจำตัวสำหรับเข้าสู่ระบบ`                                               |
| イタリア語          | `Non ci sono credenziali disponibili per l'accesso.`                                 |
| オランダ語          | `Geen inloggegevens beschikbaar.`                                                    |
| 日本語              | `ログイン用の資格情報がありません。`                                                 |
| トルコ語            | `Oturum açmak için kullanılabilecek kimlik bilgisi yok.`                             |

英語ロケールのデバイスは、通常、ASAuthorizationフレームワークがローカライズされたエラーを返す前に、APIレベルで「認証情報がない」状態を解決するため、上記に英語のバリエーションは表示されていません。プログラム上は、これらの文字列を解析するのではなく、常にコード1005で照合してください。

### 9.2 Android (Credential Manager API)

Androidは、Credential Manager API（`androidx.credentials`）を通じてパスキーエラーを表示します。エラーメッセージにはプライマリメッセージが含まれ、多くの場合、追加の詳細を示す`cause`が含まれます。iOSと比較して、Androidはより構造化されたエラータイプと、設定の問題に対するより明確な原因を提供します。

**ユーザーのキャンセルと認証情報の検出:**

| **エラー**                                        | **メモ**                                                                                                                                                                                                             |
| ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `User cancelled the operation`                    | ユーザーがパスキーのプロンプトを閉じました。`NotAllowedError`に相当します。注：Credential Managerは別のコードパスからも`User canceled the request`（USスペル）を返しますが、両者は同じです。                         |
| `Excluded credential matches existing credential` | この認証情報IDにはパスキーがすでに存在します。`InvalidStateError`に相当します。iOSとは異なり、メッセージはユーザーのキャンセルとは区別されます。                                                                     |
| `No create options available.`                    | 作成リクエストを処理できる適格なCredential Providerがありません。通常、Google Play開発者サービスが古すぎるか、パスキーの作成をサポートしているCredential Providerがないことを意味します。                            |

**構成およびセキュリティエラー:**

| **エラー**                                                                     | **メモ**                                                                                                                                                      |
| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Passkeys not supported for this app`                                          | Digital Asset Links (`assetlinks.json`) がないか、アプリの署名証明書のフィンガープリントが含まれていません。`SecurityError`に相当します。                     |
| `Https failed: respCode=301, url=https://<domain>/.well-known/assetlinks.json` | `assetlinks.json`ファイルがHTTP 200ではなくリダイレクトを返しています。Androidは、リダイレクトなしの正確なURLでファイルを要求します。                         |
| `The incoming request cannot be validated`                                     | Credential ManagerはDigital Asset Linksに対するリクエストを検証できません。                                                                                   |
| `RP ID cannot be validated.`                                                   | WebAuthnオプションのRelying Party IDが`assetlinks.json`と一致していません。                                                                                   |
| `Screen lock is missing.`                                                      | デバイスにPIN、パターン、または生体認証が設定されていません。パスキーにはユーザー検証が必要です。`ConstraintError`に相当します。                              |
| `Cannot find an eligible account.`                                             | デバイス上のGoogleアカウントで、パスキーの作成に適格なものがありません（まれであり、通常はカスタムのエンタープライズセットアップです）。                      |

**プラットフォームおよびAuthenticatorエラー:**

| **エラー**                                                                               | **メモ**                                                                                                                                                                                                                                                     |
| ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `Unsuccessful result from folsom activity.`                                              | Google Play開発者サービスの内部エラー。「Folsom」はパスキー操作用のGMSコンポーネントです。一時的なものなので、再試行が適切です。                                                                                                                             |
| `Can't find the proper key to decrypt the private key from WebauthnCredentialSpecifics.` | 同期されたパスキーは存在しますが、デバイスはその秘密鍵を復号化できません。Google Password Managerの同期状態が矛盾しています。認証情報は別のデバイスから同期されましたが、復号化キーが利用できません。開発者が対応できるものではありません。                |
| `Operation was interrupted` (cause: `The UI was interrupted - please try again.`)        | Credential ManagerのUIが別のアクティビティ（着信、画面の回転、アプリのバックグラウンド化）によって中断されました。`AbortError`に相当します。                                                                                                                 |
| `Unknown credential error`                                                               | 特定のエラータイプが当てはまらない場合の一般的なキャッチオール。通常は一時的なものです。                                                                                                                                                                     |
| `timeout` (cause: `Canceled`)                                                            | ユーザーが生体認証を完了する前に、Credential Managerの操作がタイムアウトしました。                                                                                                                                                                           |

## 10. まとめ：エラーの分類

以下の図は、上記で説明したすべてのレイヤー（インフラストラクチャ、環境、操作タイプ、分類、および検出）がエンドツーエンドでどのようにつながっているかを示しています。これは、エラー追跡を設計する際に念頭に置くべきメンタルモデルです。

```mermaid
flowchart TD
    %% Global Styles
    classDef expected fill:#e3f2fd,stroke:#1565c0,stroke-width:2px;
    classDef unexpected fill:#ffebee,stroke:#c62828,stroke-width:2px;
    classDef network fill:#fff3e0,stroke:#ef6c00,stroke-dasharray: 5 5;
    classDef env fill:#f3e5f5,stroke:#7b1fa2,stroke-width:1px;
    classDef action fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px;

    %% --- LEFT SIDE: NETWORK & SERVER ---
    subgraph Infrastructure ["インフラストラクチャ（サーバー/ネットワーク）"]
        direction TB
        ServerErr["サーバー側のエラー<br/>500 / ロジックエラー"]:::network
        NetErr["ネットワークエラー<br/>タイムアウト / 400 Bad Request"]:::network

        ServerErr & NetErr -->|次として現れる| ClientManifest["クライアント側の一般的なエラー"]
    end

    %% --- CENTER: THE ENVIRONMENT STACK (From Sketch) ---
    subgraph Environment ["環境（クライアント側）"]
        direction TB

        %% Layer 1: Platform Type
        subgraph Type ["レイヤー1：プラットフォーム"]
            Web["Web / ブラウザ"]
            Native["ネイティブ / アプリ"]
        end

        %% Layer 2: OS Context
        subgraph OS_Layer ["レイヤー2：OSとバージョン"]
            OS_Web["OS: Windows, macOS, Linux"]
            OS_Nat["OS: iOS, Android"]
            OS_Ver["OSのバージョン / 設定<br/>（画面ロック、生体認証）"]

            Web --> OS_Web
            Native --> OS_Nat
            OS_Web & OS_Nat --> OS_Ver
        end

        %% Layer 3: Client Software
        subgraph Client_Layer ["レイヤー3：クライアントソフトウェア"]
            Browser["ブラウザ: Chrome, Safari, Edge<br/>+ バージョン"]
            App["ネイティブアプリ / WebView<br/>+ アプリのバージョン"]

            OS_Ver --> Browser
            OS_Ver --> App
        end

        %% Layer 4: WebAuthn Operation
        subgraph Op_Layer ["レイヤー4：WebAuthn操作"]
            OpType{"操作タイプ"}

            Login["ログイン<br/>(navigator.credentials.get)"]
            Reg["登録<br/>(navigator.credentials.create)"]

            Browser & App --> OpType
            OpType --> Login
            OpType --> Reg
        end

        %% Layer 5: Sub-types & Complexity
        subgraph Modality ["レイヤー5：モダリティと複雑さ"]
            Sub_CUI["CUI / 条件付きUI"]
            Sub_Auto["自動操作"]
            Sub_CDA["CDA / クロスデバイス認証"]

            Login & Reg --> Sub_CUI
            Login & Reg --> Sub_Auto
            Login & Reg --> Sub_CDA
        end
    end

    %% --- BOTTOM: CLASSIFICATION & ACTION (The "What here?" section) ---
    subgraph Analysis ["エラー分析とランタイム"]
        direction TB

        %% Classification
        Classification{"分類"}

        Exp["想定内のエラー<br/>（ユーザーがセレモニーを中止）"]:::expected
        Unexp["予期しないエラー<br/>（システム/クラッシュ/不明）"]:::unexpected

        ClientManifest --> Classification
        Sub_CUI & Sub_Auto & Sub_CDA --> Classification

        Classification --> Exp
        Classification --> Unexp

        %% Detection Logic
        subgraph Detection ["検出目標"]
            P1["P1：ログインの問題"]
            P2["P2：作成の問題"]
            Baseline["ベースラインの逸脱を検出<br/>（想定内エラーの増加）"]
            NewErr["新しい異常を検出<br/>（予期しないエラーの増加）"]
        end

        Exp --> Baseline
        Unexp --> NewErr
        Exp & Unexp --> P1 & P2

        %% Actionable Outcomes
        subgraph Action ["緩和策"]
            Fix["ホットフィックス/新バージョンのデプロイ"]:::action
            Fallback["自動フォールバックのアクティブ化<br/>（パスキーの無効化）"]:::action
        end

        P1 & P2 & NewErr --> Fix
        P1 & P2 & NewErr --> Fallback
    end

    %% Connectors
    Infrastructure -.-> Environment
```

重要な洞察：生のエラー名（`error.name`）は、環境のどのレイヤーがそれを生成したか、どの操作が実行されていたか、そしてそのエラーが想定内か予期しないものかを知って初めて意味を持ちます。以下のセクションでは、何を記録し、どのように対処するかについて説明します。

## 11. エラーをデバッグ可能にするためにログに記録すべき内容

この記事でのエラー分類のほとんどは、クライアント側のシグナルだけで実行できます。フロントエンドのみのオブザーバビリティSDKは、WebAuthnエラーの大部分を分類するのに十分なコンテキストをキャプチャします。これは、CorbadoのオブザーバビリティSDKの設計でもあります。クライアント側のレイヤーが、エラーの原因特定、タイミング、操作コンテキスト、およびプラットフォーム検出を処理します。サーバー側のログは、バックエンドだけが見ることができる失敗に対して第2のレイヤーを追加します。

主な要件：すべての試行はエンドツーエンドで結合可能でなければなりません。共有された相関ID（例：`auth_flow_id`）により、クライアント側のコンテキストとサーバーの検証結果が結び付けられます。

### 11.1 クライアント側のシグナル（フロントエンドSDK）

| **シグナル**                               | **重要である理由**                                                                                                                             |
| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `error.name` + 正規化された理由の分類      | 生のブラウザエラー + あなたの分類                                                                                                              |
| 操作タイプ                                 | 条件付きUI、モーダルログイン、手動での作成、条件付き作成、自動追加。**CDA（クロスデバイス認証）**はそれ自体が複雑なものです。                  |
| 完全な環境のコンテキスト                   | OS + バージョン、ブラウザ + バージョン、**ハードウェアのブランド/モデル**、**Authenticatorの設定**（例：GPMは有効か？）                      |
| Authenticator / Credential Managerのコンテキスト | 拡張機能およびプロバイダーの破損                                                                                                               |
| 操作開始からのエラー発生までの時間         | 即時の拒否（1秒未満）対ユーザーのキャンセル（1〜15秒）対タイムアウト（30秒以上）                                                               |
| ネットワーク / 接続状態                    | ネットワークエラーはクライアントエラーとして現れることがよくあります。ユーザーがオフラインであったかどうかを追跡し、接続が復元したときに送信するように**ログをキューに格納**します。 |
| QR/ハイブリッドUIが表示されたかどうか      | ローカルの失敗とクロスデバイスの失敗                                                                                                           |
| 相関ID（`auth_flow_id`）                   | サーバーのログと結合する                                                                                                                       |

### 11.2 サーバー側のシグナル（バックエンドの検証）

サーバーの検証の失敗は、ブラウザが認証情報と署名付きのチャレンジを返した後に発生します。これらは、クライアント側の`DOMException`名と同じ分類に含めるのではなく、明示的なコードを使用した構造化されたエラーにする必要があります。WebAuthnのサーバーの実装を参照してください。

| **シグナル**                               | **重要である理由**                                                                                                                                                                                  |
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| チャレンジの不一致 / 期限切れのチャレンジ  | セッションのタイミングまたはリプレイの問題                                                                                                                                                          |
| オリジン / RP IDの不一致                   | マルチドメインの設定バグ                                                                                                                                                                            |
| 無効な署名 / 認証情報が見つからない        | 削除または破損した認証情報。よくあるケース：ユーザーがサーバー側ですでに削除したパスキーを使用した条件付きUIログイン。Signal APIを使用して、クライアントとサーバーの認証情報リストを同期させます。 |
| ユーザーハンドルの不一致                   | アカウントのマッピングの問題                                                                                                                                                                        |
| 相関ID（`auth_flow_id`）                   | クライアント側のコンテキストと結合する                                                                                                                                                              |

（ステップごとの離脱やステップ間のコンバージョンなど）完全なファネルモデルが必要な場合は、離脱を理解するためのパスキーテレメトリを参照してください。

[Watch on YouTube](https://www.youtube.com/watch?v=wnrXJzvBjsU)

このデータが整備されると、結論はシンプルになります。ほとんどの「エラー」は、UXの修正、カバレッジの修正、または設定の修正のいずれかになります。しかし、この分類を自分で構築して維持することは、継続的で多大な作業となります。

## 12. エラー名を超えて：Corbadoが生のエラーをアクション可能なシグナルに変換する方法

上記のログチェックリストは生のシグナルをキャプチャします。大規模な本番環境では、`error.name`だけでは十分ではありません。この分類を自分で構築することは、継続的で多大な作業となります。エラーメッセージはブラウザやOSのリリースごとに変更され、パスワードマネージャーのプロバイダーはセレモニーの動作を変更するアップデートを提供し、機能がリリースされるたびに新しいエラーのシグネチャが現れます。

### 12.1 `error.name`だけでは不十分な理由

ブラウザが分離してくれない3つの側面に応じて、同じ`NotAllowedError`でも6つの異なる意味を持つ可能性があります。

| **次元**                     | **ブラウザが提供するもの** | **実際に必要なもの**                                                                       | **例**                                                                                                        |
| ---------------------------- | -------------------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- |
| **操作のコンテキスト**       | `NotAllowedError`          | 条件付きUI、モーダルログイン、手動作成、条件付き作成、または自動追加のどれだったか？       | Androidは、ログインの却下（想定内）と作成の失敗（予期しない）で同じ「不明なエラー」を返します                 |
| **タイミング**               | 所要時間のデータなし       | 即時（1秒未満）対ユーザーのキャンセル（1〜15秒）対タイムアウト（30秒以上）                 | 200ms = 環境の拒否、5s = ユーザーがダイアログを見てキャンセル、35s = タイムアウト                             |
| **プラットフォーム + Authenticator** | 一般的な`error.name`       | すべてのエラーについてのOS、ブラウザ、バージョン、Credential Manager                       | Chromeの「ユーザーがダイアログを閉じた」とSafariの「自動入力が利用できない」はどちらも`NotAllowedError`として表面化します |

### 12.2 CorbadoのオブザーバビリティSDKがキャプチャするもの

これは、[CorbadoのオブザーバビリティSDK](https://www.corbado.com/pricing)が解決するために構築された問題です。これは既存のパスキー実装の上に配置される軽量なフロントエンド統合であり、任意のWebAuthnサーバーと任意のIDPで動作し、すべてのWebAuthnエラーを3つの次元すべてにわたって自動的に分類します。

| **機能**                             | **実行内容**                                                                                                                                                                                                                                                                       |
| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **エラーの原因特定**                 | セレモニーの試行ごとに、OS、OSのバージョン、ブラウザ、ブラウザのバージョン、およびAuthenticatorをキャプチャします。                                                                                                                                                                |
| **操作モード**                       | 各エラーを特定の操作（条件付きUI、モーダルログイン、手動での作成、条件付き作成、自動追加）に結び付け、同じ`NotAllowedError`を異なる根本原因へと解決します。                                                                                                                        |
| **アクション開始からのタイミング**   | セレモニーの開始から所要時間を記録し、推測することなく、即時の拒否、ユーザーのキャンセル、タイムアウトを区別します。                                                                                                                                                               |
| **インテリジェントなエラー分類**     | （名前だけでなく）完全なエラーコンテキストで一致させ、多様な**環境**（OS、ハードウェア、設定）にわたって正規化します。**CDA**とローカルなどの個別のエラーグループを優先し、**想定内（ユーザーによる中止）**と**予期しない（システム障害）**エラーを区別します。                 |
| **パスワードマネージャーの断片化**   | Credential Manager拡張機能（Bitwarden、1Password、LastPass）がセレモニーを傍受し、非標準のレスポンスを返したことを検出し、拡張機能による障害をプラットフォームの障害から分離します。                                                                                               |

これは**Observe**（監視）レイヤーであり、実装を変更することなく何が起こっているかを可視化します。

### 12.3 2つのデバッグ方法：トップダウンとボトムアップ

Corbadoの管理コンソールは、2つの調査パスをサポートしています。

**トップダウン（ダッシュボードから根本原因へ）：**

1. **2つの明確な異常タイプを監視します。**
    - **想定内エラーの増加（ベースラインの逸脱）：** 特定の環境（例：iPhone 15のiOS 18.2）で「ユーザーのキャンセル」が徐々に増加していませんか？これは多くの場合、OSのアップデートによって導入されたUXの摩擦を示しています。
    - **予期しないエラーの増加（急増）：** まったく新しいエラーや、障害の急増。これは通常、破壊的な変更（内部IDPスタックの更新）や、新しいブラウザのバージョンでの後退を示しています。
2. **影響度による優先順位付け：**
    - **P1：ログインの問題。** ログインの成功率が低下した場合は、直ちにアラートを出します。
    - **P2：作成の問題。** トレンドは監視しますが、作成フローにおける「ユーザーのキャンセル」の急増でエンジニアを呼び出すことは避けます。
3. **環境のドリルダウン：** 詳細な次元（ハードウェア、認証設定）を使用して、問題がグローバルなものか、「Android 14を搭載したSamsungデバイス」に特有のものかを切り分けます。
4. **緩和策：** 重大な急増が検出された場合は、**キルスイッチ**を準備しておきます。その特定の環境で自動または手動でパスキーを無効にし、OTP/パスワードにフォールバックして、調査中にログイン率を維持します。

**ボトムアップ（エラーパターンから影響度へ）：**

1. エラー分類ビューから開始します。分類されたエラーパターンとその量を確認します。
2. 新しいパターンが現れたら、エラーのマッピングを調整します（例：新しいブラウザのバージョンが異なるエラーメッセージを返したなど）。
3. エラーはKPIの変更と相互に相関し、ダッシュボード上の注釈として表示されるため、特定のエラーパターンの急増は影響を与えた指標と自動的にリンクされます。

両方のパスは集束します。トップダウンは「何かが間違っていること」を示し、ボトムアップは「その理由」を示します。[AIアナリティクスアシスタント](https://www.corbado.com/ai-analytics-assistant)は、エラーデータと導入指標の両方について自然言語で質問できるようにすることで、この2つを結び付けます。

これらのシグナルに基づいて行動したいチームは**Adopt**に移行し、[パスキーインテリジェンス](https://docs.corbado.com/corbado-connect/features/passkey-intelligence)を追加して、自動的にセレモニーを制限し、登録プロンプトを最適化し、壊れたパスキーの状態を修復することができます。規制環境やハイパースケールの導入では、**Enterprise**によりシングルテナントのホスティング、SIEMの統合、PSD2準拠の設定が追加されます。

## 13. 結論

WebAuthnのエラー名は結果ではありません。これらはヒントであり、操作タイプ、タイミング、プラットフォームのコンテキストと結び付けて初めて実行可能なものになります。

- **本番環境において、最も一般的なWebAuthnエラー名は何を意味するのか？** ほとんどは、ユーザー制御のフロー（`NotAllowedError`）、アプリのライフサイクル/並行処理（`AbortError`）、セキュリティコンテキスト/設定（`SecurityError`）、またはオプション/状態のバグ（`InvalidStateError`、`ConstraintError`、`DataError`）という少数のレイヤーにマッピングされます。ボリュームの大部分は`NotAllowedError`であり、そのほとんどは想定内の動作（ユーザーがプロンプトを閉じた）です。
- **`NotAllowedError`をどのように明確化するか？** タイミング（即時の拒否、ユーザーのキャンセル、タイムアウト）、QR/ハイブリッドインジケーター（可用性の不一致）、ユーザーアクティベーションのコンテキスト（特にiOS/Safari）、および操作タイプ（条件付きUI、モーダルログイン、パスキー作成、条件付き作成）を使用します。すべての`NotAllowedError`を同じ失敗モードとして扱わないでください。
- **なぜ操作タイプが重要なのか？** 条件付きUIログイン中の同じ`error.name`は、条件付き作成や手動でのパスキー作成（ユーザーがダイアログを閉じた）とは完全に異なるシグナルです。エラーとともに操作タイプを記録することで、一般的な`NotAllowedError`がアクション可能な分類に変わります。
- **エラーをデバッグ可能にするための最低限のコンテキストは何か？** `error.name`、操作タイプ、操作開始からの時間、フロータイプ、QR/ハイブリッドUIが表示されたかどうか、OS/ブラウザ/デバイス（バージョンを含む）、相関ID（`auth_flow_id`）、および明示的なコードとしてのサーバーの検証拒否をキャプチャします。

すべてのエラータイプに共通する2つの経験則は、「生のブラウザエラーをユーザーに表示しないこと（常に明確なフォールバックパスを提供すること）」、そして「失敗の理由が異なり、異なる修正が必要となるため、ローカルでの試行とQR/ハイブリッドでのクロスデバイスの試行を分けること」です。大規模な環境では、ブラウザ、OSバージョン、Credential Managerにまたがるエラー分類の維持は継続的な作業となります。ゼロから構築するのではなく、維持されたパターンライブラリを備えたオブザーバビリティSDKの使用を検討してください。

## よくある質問

### パスキーのプロンプトをキャンセルしたユーザーと、デバイスにパスキーがない場合の違いをどのように見分ければよいですか？

Webでは、プライバシー上の理由からブラウザが両方のケースを`NotAllowedError`に統合するため、直接区別することはできません。タイミングを代用として使用してください。1秒未満のエラーは通常、環境による拒否または機能がないことを意味し、1〜15秒はユーザーがダイアログを見て閉じたことを示唆します。ネイティブのiOSおよびAndroidアプリでは、リクエストの前に`preferImmediatelyAvailableCredentials`を設定することで、UIが表示される前にクリーンな「認証情報がない」シグナル（iOSのコード1005、Androidの`GetCredentialRequest`）を得ることができます。

### パスキーがほとんどのユーザーで機能しているように見えるのに、WebAuthnのエラー率が非常に高いのはなぜですか？

最適化された大規模なパスキーの導入環境では、記録されるWebAuthnエラーの95%以上はシステム障害ではなく、想定内のユーザーによる中止です。「ユーザーがプロンプトを閉じた」ことを真の失敗から分離せずに、生の`error.name`のカウントを追跡すると、エラーの指標が膨れ上がり、`NotAllowedError`の量の中に隠された実際の後退を覆い隠してしまいます。カウントを操作タイプごとに分け、ユーザーのキャンセル分類を`SecurityError`、`ConstraintError`、`DataError`のような予期しないエラーとは別に扱ってください。

### iOSでのASAuthorizationErrorのコード1005は何を意味し、どのように使用すべきですか？

iOSのコード1005（`NotInteractive`）は、`ASAuthorizationController`で`preferImmediatelyAvailableCredentials`が設定されたときにデバイスで利用できるパスキーがなく、ユーザーにUIが表示されなかったことを意味します。これは、プライバシーの制約によりWebブラウザが提供できないクリーンな「このデバイスにはパスキーが存在しない」というシグナルです。Appleのメッセージは30以上の言語にローカライズされており、OSバージョン間で変更される可能性があるため、`localizedDescription`ではなく、常に数値コードで分類してください。

### 条件付き作成または自動トリガーによるパスキー追加フロー中のNotAllowedErrorをどのように処理すべきですか？

条件付き作成中、`NotAllowedError`、`AbortError`、`InvalidStateError`はすべて想定内の結果であり、エラーとして表示するのではなく、静かに無視すべきです。`NotAllowedError`は自動入力が利用できないか、ページがフォーカスを失ったことを示し、`InvalidStateError`はプロバイダーにパスキーがすでに存在することを示します。呼び出しを試行する前に、`getClientCapabilities()`を使用して`conditionalCreate`のサポートを検証し、ドキュメントの表示状態を確認して、エラーのカウントが膨らむのを防いでください。

### WebAuthnの失敗を実際にデバッグ可能にするために、error.nameと一緒にどのようなコンテキストをログに記録すべきですか？

操作タイプ（条件付きUI、モーダルログイン、手動作成、条件付き作成、または自動追加）、操作開始からエラーまでの時間、OSバージョン、ブラウザバージョン、ハードウェアモデル、QR/ハイブリッドUIが表示されたかどうか、およびサーバー側のログと結合するための相関IDをキャプチャしてください。チャレンジの不一致、無効な署名、認証情報が見つからないなどのサーバー検証の失敗は、クライアント側の`DOMException`名と同じ分類に含めるのではなく、明示的な構造化されたコードとしてログに記録する必要があります。これらのシグナルを組み合わせることで、推測することなく、エラーの大部分をアクション可能な分類に分けることができます。
