nextauthjs/next-auth · error
Authenticator not found.
Error message
Authenticator not found.
What it means
SQLite twin of the pg adapter guard: updateAuthenticatorCounter updates the WebAuthn counter by credentialID and returns the row via RETURNING. When no authenticator matches the credentialID, it throws 'Authenticator not found.'
Source
Thrown at packages/adapter-drizzle/src/lib/sqlite.ts:330
.where(eq(authenticatorsTable.credentialID, credentialID))
.then((res) => res[0] ?? null) as Awaitable<AdapterAuthenticator | null>
},
async listAuthenticatorsByUserId(userId: string) {
return client
.select()
.from(authenticatorsTable)
.where(eq(authenticatorsTable.userId, userId))
.then((res) => res) as Awaitable<AdapterAuthenticator[]>
},
async updateAuthenticatorCounter(credentialID: string, newCounter: number) {
const authenticator = await client
.update(authenticatorsTable)
.set({ counter: newCounter })
.where(eq(authenticatorsTable.credentialID, credentialID))
.returning()
.then((res) => res[0])
if (!authenticator) throw new Error("Authenticator not found.")
return authenticator as Awaitable<AdapterAuthenticator>
},
}
}
type DefaultSQLiteColumn<
T extends {
data: string | boolean | number | Date
dataType: "string" | "boolean" | "number" | "date"
notNull: boolean
isPrimaryKey?: boolean
columnType:
| "SQLiteText"
| "SQLiteBoolean"
| "SQLiteTimestamp"
| "SQLiteInteger"
},View on GitHub (pinned to a1a16a5a77)
Solutions
- Verify the credential exists (query authenticatorsTable by credentialID) before updating.
- Re-register the passkey/credential against this database.
- Ensure credentialID column type (text vs blob) matches the value format passed in — normalize to a consistent encoding (e.g. base64url string).
- Confirm you are pointed at the same SQLite database used during registration.
Example fix
// before
await adapter.updateAuthenticatorCounter(rawCredId, counter)
// after
const credId = toBase64UrlString(rawCredId)
const existing = await db.select().from(authenticatorsTable)
.where(eq(authenticatorsTable.credentialID, credId))
if (!existing.length) throw new Error(`credential ${credId} not registered`)
await adapter.updateAuthenticatorCounter(credId, counter) Defensive patterns
Strategy: validation
Validate before calling
const rows = await db.select().from(authenticatorsTable)
.where(eq(authenticatorsTable.credentialID, credentialID))
if (rows.length === 0) throw new Error(`credentialID ${credentialID} not registered`)
await adapter.updateAuthenticatorCounter(credentialID, counter) Try / catch
try {
await adapter.updateAuthenticatorCounter(credentialID, counter)
} catch (e) {
if ((e as Error).message === 'Authenticator not found.') {
console.warn(`Credential ${credentialID} unknown; require re-enrollment`)
return
}
throw e
} Prevention
- Keep credentialID encoding consistent (text column + base64url strings recommended).
- Reset browser passkeys whenever you reseed or swap the SQLite database file.
- Gate counter updates on a prior successful registration lookup.
When it happens
Trigger: updateAuthenticatorCounter(credentialID, newCounter) where the credentialID is absent from the SQLite authenticators table — credential never registered, deleted, or stored with different encoding.
Common situations: Deleting the SQLite file (or reseeding) while browsers retain registered passkeys; credentialID stored as BLOB but compared as string (or vice versa) so the eq() match fails; switching between pg and sqlite adapters where credentials were only registered in one.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
AI-assisted analysis of nextauthjs/next-auth@a1a16a5a77 (2026-08-28).
Data as JSON: /api/errors/30db54f502c0c0e6.
Report an issue: GitHub.