Skip to content
ecdsa.com
WebAuthn navigator.credentials.create() with excludeCredentialsWebAuthn and passkeys

Error message

InvalidStateError: The user attempted to register an authenticator that contains one of the credentials already registered with the relying party.

Chromium's text is above; other browsers word it differently. The DOMException name `InvalidStateError` is the stable part.

WebAuthn navigator.credentials.create() with excludeCredentials — DOMException InvalidStateError (Chromium)

What it means

Registration was refused because the authenticator already holds one of the credentials you listed in `excludeCredentials`. Relying parties send that list precisely to prevent registering the same authenticator twice for one account, so this is usually the intended outcome — the user already has a working passkey on this device. Browsers only reveal it after the user has interacted with the authenticator.

Why it happens

How to fix it

  1. 1.

    Tell the user they are already set up

    Treat InvalidStateError as "a passkey already exists here" and lead the user to sign in with it.

    js
    try {
      const credential = await navigator.credentials.create({ publicKey: options });
      await saveCredential(credential);
    } catch (e) {
      if (e.name === "InvalidStateError") {
        showMessage("This device already has a passkey for your account. You can use it to sign in.");
      } else if (e.name !== "NotAllowedError") {
        throw e;
      }
    }
  2. 2.

    Exclude only the current user's credentials

    Build `excludeCredentials` from the signed-in user's stored credential IDs, and use a stable random `user.id` per account.

    js
    // Server: exclude only the CURRENT user's credentials (base64url ids from your database).
    const options = await generateRegistrationOptions({
      rpName: "Example",
      rpID,
      userName: user.email,
      userID: user.webauthnUserId,              // stable per user, never the email
      excludeCredentials: user.passkeys.map((p) => ({ id: p.credentialId, transports: p.transports })),
    });

Related errors

Browse the full signature error database