FIDO2/WebAuthn Error Codes

DOMException names thrown by WebAuthn APIs with cause and user-visible message

Searchable reference for WebAuthn DOMException error names — NotAllowedError, InvalidStateError, NotSupportedError, SecurityError and more — with the trigger condition and a recommended user-facing message for each.

Why is every WebAuthn failure a NotAllowedError?

For privacy, the spec deliberately collapses many distinct failures — user cancelled, timeout, no matching credential, user-verification refused — into a single NotAllowedError so a site cannot probe which authenticators a user has. You generally cannot tell these apart programmatically and should show one neutral retry message.

WebAuthn and FIDO2 surface failures as DOMException objects whose name property tells you what went wrong. This reference lists the names thrown by navigator.credentials.create() and navigator.credentials.get(), what triggers each, and a clear message to show the user.

How it works

A WebAuthn ceremony returns a rejected promise on failure. You inspect the rejection’s name:

try {
  const cred = await navigator.credentials.get({ publicKey });
} catch (err) {
  if (err.name === "NotAllowedError") {
    // user cancelled, timed out, or no credential matched
  }
}

The platform intentionally limits how much detail it exposes. Most user-driven failures and timeouts collapse into a single NotAllowedError so that a website cannot fingerprint which authenticators or passkeys a person has. The errors you can distinguish are mostly developer mistakes — a wrong rp.id, an insecure context, an already-registered key, or an algorithm the authenticator does not support.

Notes and examples

  • InvalidStateError during registration is not a bug — it means excludeCredentials matched an existing credential, i.e. the user is already enrolled. Treat it as “already registered”.
  • SecurityError almost always means your relying party ID does not match the origin, or the page is not a secure context.
  • Never display a raw DOMException string. Map the name to one of the user messages below.
  • Because cancel and timeout both produce NotAllowedError, always provide a retry path rather than a dead end.