nextauthjs/next-auth · error

Verification Token not created

Error message

Verification Token not created

What it means

createVerificationToken() stores an email verification / magic-link token. If the SurrealDB create fails or returns an empty result, the adapter throws 'Verification Token not created' after swallowing the underlying error.

Source

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

    async createVerificationToken(verificationToken: VerificationToken) {
      try {
        const surreal = await client
        const doc = verificationTokenToDoc(verificationToken)

        const verificationTokenDocs = await surreal.create<
          VerificationTokenDoc,
          Omit<VerificationTokenDoc, "id">
        >("verification_token", doc)
        if (verificationTokenDocs.length) {
          const verificationTokenDoc: Partial<VerificationTokenDoc> =
            verificationTokenDocs[0]
          if (verificationTokenDoc.id) delete verificationTokenDoc.id
          return docToVerificationToken(
            verificationTokenDoc as VerificationTokenDoc
          )
        }
      } catch {}
      throw new Error("Verification Token not created")
    },
    async useVerificationToken({
      identifier,
      token,
    }: {
      identifier: string
      token: string
    }) {
      const surreal = await client
      try {
        const [tokens] = await surreal.query<[VerificationTokenDoc[]]>(
          `SELECT * FROM verification_token WHERE identifier = $identifier AND token = $vt LIMIT 1`,
          { identifier, vt: token }
        )
        if (tokens.length && tokens.at(0)) {
          const vt = tokens[0]
          if (vt) {
            await surreal.delete(vt.id)

View on GitHub (pinned to a1a16a5a77)

Solutions

  1. Confirm the verification token table exists and the adapter credentials can create rows in it
  2. Verify SurrealDB connection env vars (URL, namespace, database, auth) are correct
  3. Log inside the try to reveal the suppressed root error
  4. Check that identifier/token/expires values passed by the core are valid (non-null, correct types)
Defensive patterns

Strategy: try-catch

Validate before calling

if (!identifier || !token || !(expires instanceof Date)) {
  throw new Error('Invalid verification token payload')
}
await adapter.createVerificationToken({ identifier, token, expires })

Try / catch

try {
  return await adapter.createVerificationToken(params)
} catch (e) {
  if (e.message === 'Verification Token not created') {
    console.error('Token creation failed — check SurrealDB table/permissions', e)
  }
  throw e
}

Prevention

When it happens

Trigger: surreal.create('verification_token', ...) throws (connection/permission/schema) or returns an empty array; malformed token payload fields (identifier/token/expires) after userToDoc-style conversion.

Common situations: Email magic-link or email-verification flows failing at sign-in; SurrealDB table missing because migrations were never run; adapter lacking CREATE permission on the verification token table.

Related errors


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