nextauthjs/next-auth · error · AuthError
WebAuthn authenticator not found in database: ${JSON.stringi
Error message
WebAuthn authenticator not found in database: ${JSON.stringify({credentialID})} What it means
After normalizing the credential ID, verifyAuthenticate looks up the authenticator in the database via adapter.getAuthenticator(). If no stored credential matches the credentialID from the assertion, this error is thrown because authentication cannot proceed against an unknown credential.
Source
Thrown at packages/core/src/lib/utils/webauthn-utils.ts:230
request.body && typeof request.body.data === "string"
? (JSON.parse(request.body.data) as unknown)
: undefined
if (
!data ||
typeof data !== "object" ||
!("id" in data) ||
typeof data.id !== "string"
) {
throw new AuthError("Invalid WebAuthn Authentication response")
}
// Reset the ID so we smooth out implementation differences
const credentialID = toBase64(fromBase64(data.id))
// Get authenticator from database
const authenticator = await adapter.getAuthenticator(credentialID)
if (!authenticator) {
throw new AuthError(
`WebAuthn authenticator not found in database: ${JSON.stringify({
credentialID,
})}`
)
}
// Get challenge from request cookies
const { challenge: expectedChallenge } = await webauthnChallenge.use(
options,
request.cookies,
resCookies
)
// Verify the response
let verification: VerifiedAuthenticationResponse
try {
const relayingParty = provider.getRelayingParty(options, request)
verification = await provider.simpleWebAuthn.verifyAuthenticationResponse({View on GitHub (pinned to a1a16a5a77)
Solutions
- Confirm the authenticator exists via adapter.getAuthenticator(credentialID) with the exact normalized base64 ID
- Check that your adapter's getAuthenticator compares/stores IDs in the same encoding used here
- Point the app at the same database where registration stored the credential
- Re-register the passkey if the server-side record was deleted
Example fix
// before const auth = await adapter.getAuthenticator(rawId) // wrong encoding // after const auth = await adapter.getAuthenticator(toBase64(fromBase64(rawId)))
Defensive patterns
Strategy: validation
Validate before calling
const credentialID = toBase64(fromBase64(data.id))
const authenticator = await adapter.getAuthenticator(credentialID)
if (!authenticator) {
// prompt re-registration instead of calling verifyAuthenticate
} Try / catch
try {
await verifyAuthenticate(data)
} catch (e) {
if (e instanceof AuthError && e.message.includes('authenticator not found')) {
// offer the user to register a new passkey
}
} Prevention
- Use one consistent database across environments
- Store and compare credential IDs in the same base64 encoding
- Clean up browser credentials when deleting them server-side
- Add integration tests covering register-then-login round trips
When it happens
Trigger: A passkey assertion is presented whose credentialID does not exist in the adapter's authenticator table — e.g. credential deleted, different database/environment, or ID encoding mismatch.
Common situations: Switching databases or environments between registration and login; user deleted the passkey server-side but the browser still offers it; custom adapters with getAuthenticator not implemented or comparing raw vs base64-encoded IDs.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Failed to update authenticator counter. This may cause futur
- WebAuthn account not found in database: ${JSON.stringify({cr
- An adapter is required for the WebAuthn provider
- WebAuthn provider requires a database adapter to be configur
- Unable to update authenticator with credential ${credentialI
AI-assisted analysis of nextauthjs/next-auth@a1a16a5a77 (2026-08-28).
Data as JSON: /api/errors/1a9968a887857a77.
Report an issue: GitHub.