nextauthjs/next-auth · error

Verification Token not found

Error message

Verification Token not found

What it means

Despite its message, this error is thrown at the end of listAuthenticatorsByUserId() — a copy-paste mislabeled message. When the query for authenticators belonging to a user throws, the adapter throws 'Verification Token not found'. The underlying cause is swallowed by `catch {}`.

Source

Thrown at packages/adapter-surrealdb/src/index.ts:532

        if (authenticatorDoc.length) {
          return docToAuthenticator(authenticatorDoc[0])
        }
      } catch {}
      return null
    },
    async listAuthenticatorsByUserId(userId: AdapterAuthenticator["userId"]) {
      const surreal = await client
      try {
        const [authenticatorDocs] = await surreal.query<[AuthenticatorDoc[]]>(
          `SELECT * FROM authenticator WHERE userId = $userId LIMIT 1`,
          {
            userId,
          }
        )

        return authenticatorDocs.map((v) => docToAuthenticator(v))
      } catch {}
      throw new Error("Verification Token not found")
    },
    async updateAuthenticatorCounter(
      credentialId: AdapterAuthenticator["credentialID"],
      newCounter: AdapterAuthenticator["counter"]
    ) {
      try {
        if (!credentialId) throw new Error("credential id is required")
        const surreal = await client
        const [authenticatorDoc] = await surreal.query<[AuthenticatorDoc]>(
          `UPDATE ONLY authenticator MERGE $doc WHERE credentialID = $cid`,
          {
            cid: credentialId,
            doc: {
              counter: newCounter,
            },
          }
        )
        return docToAuthenticator(authenticatorDoc)

View on GitHub (pinned to a1a16a5a77)

Solutions

  1. Ignore the message text — debug the authenticator table and userId instead
  2. Verify the `authenticator` table exists and the adapter can SELECT from it
  3. Check the userId passed matches stored doc userId values (same id format)
  4. Note the message is a copy-paste bug in the adapter; log inside the try to see the real error
Defensive patterns

Strategy: try-catch

Validate before calling

if (!userId) throw new Error('listAuthenticatorsByUserId requires a userId')

Try / catch

try {
  return await adapter.listAuthenticatorsByUserId(userId)
} catch (e) {
  // NOTE: message says 'Verification Token not found' but failure is in authenticator listing
  console.error('listAuthenticatorsByUserId failed (misleading message)', e)
  return [] // or rethrow depending on flow
}

Prevention

When it happens

Trigger: SELECT * FROM authenticator WHERE userId = $userId throws (connection/permission); authenticatorDocs is undefined so .map() throws a TypeError caught by the empty catch and converted to this error.

Common situations: WebAuthn sign-in listing a user's passkeys while the authenticator table is missing or permissions deny SELECT; userId type/format mismatch with stored docs; copy-paste bug makes debugging confusing because the message points at verification tokens.

Related errors


AI-assisted analysis of nextauthjs/next-auth@a1a16a5a77 (2026-08-28). Data as JSON: /api/errors/5c74ea3f76c297bb. Report an issue: GitHub.