QuantumNous/new-api · warning · Error

Passkey verification is not available in the current state

Error message

Passkey verification is not available in the current state

What it means

Thrown from the catch in verifyPasskey() when the WebAuthn call rejects with a DOMException named InvalidStateError. The spec defines this for operations on credentials that are in an invalid state — most commonly re-using a credential that is already registered, or the authenticator being in a state that forbids the requested operation. The original exception is kept as cause.

Source

Thrown at web/src/features/auth/secure-verification/api.ts:184

    if (!finishResponse.success) {
      throw new Error(
        finishResponse.message || i18next.t('Passkey verification failed')
      )
    }

    if (!finishResponse.data?.proof_token) {
      throw new Error(i18next.t('Verification proof was not returned'))
    }
    return finishResponse.data
  } catch (error: unknown) {
    if (error instanceof DOMException && error.name === 'NotAllowedError') {
      throw new Error(
        i18next.t('Passkey verification was cancelled or timed out'),
        { cause: error }
      )
    }
    if (error instanceof DOMException && error.name === 'InvalidStateError') {
      throw new Error(
        i18next.t('Passkey verification is not available in the current state'),
        { cause: error }
      )
    }
    throw error
  }
}

View on GitHub (pinned to e2c7aa7b10)

Solutions

  1. Make the passkey button idempotent: disable it while a ceremony is in flight so only one credentials.get() runs.
  2. Retry once — transient authenticator state usually clears.
  3. Unplug/replug the security key or re-trigger platform-authenticator availability.
  4. If persistent, re-register the passkey (the stored credential may be in a bad state server-side).

Example fix

// before
<button onClick={handlePasskey}>Use Passkey</button>

// after
const [busy, setBusy] = useState(false)
<button
  disabled={busy}
  onClick={async () => {
    setBusy(true)
    try { await verify('passkey', scope) } finally { setBusy(false) }
  }}
>
  Use Passkey
</button>
Defensive patterns

Strategy: try-catch

Validate before calling

// guard against concurrent ceremonies
if (busyRef.current) return
busyRef.current = true
try {
  await verify('passkey', scope)
} finally {
  busyRef.current = false
}

Type guard

const isInvalidStateError = (e: unknown): boolean =>
  e instanceof DOMException && e.name === 'InvalidStateError'

Try / catch

try {
  await verify('passkey', scope)
} catch (e) {
  if (isInvalidStateError(e)) {
    await sleep(250)
    return verify('passkey', scope) // single retry after authenticator settles
  }
  throw e
}

Prevention

When it happens

Trigger: navigator.credentials.get() with an allowList credential whose state is invalid on the authenticator; racing a second credentials.get() while one is pending on some browsers; platform authenticator locked by a prior in-flight ceremony.

Common situations: Double-triggered verification (button double-click firing two ceremonies); security key left in a half-finished state from a previous aborted flow (replug fixes it); browser bug after tab sleep/resume.

Related errors


AI-assisted analysis of QuantumNous/new-api@e2c7aa7b10 (2026-08-15). Data as JSON: /api/errors/24e8e69c60d0497a. Report an issue: GitHub.