{"record":{"id":"30db54f502c0c0e6","repo":"nextauthjs/next-auth","slug":"authenticator-not-found-30db54","errorCode":null,"errorMessage":"Authenticator not found.","messagePattern":"Authenticator not found\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/adapter-drizzle/src/lib/sqlite.ts","lineNumber":330,"sourceCode":"        .where(eq(authenticatorsTable.credentialID, credentialID))\n        .then((res) => res[0] ?? null) as Awaitable<AdapterAuthenticator | null>\n    },\n    async listAuthenticatorsByUserId(userId: string) {\n      return client\n        .select()\n        .from(authenticatorsTable)\n        .where(eq(authenticatorsTable.userId, userId))\n        .then((res) => res) as Awaitable<AdapterAuthenticator[]>\n    },\n    async updateAuthenticatorCounter(credentialID: string, newCounter: number) {\n      const authenticator = await client\n        .update(authenticatorsTable)\n        .set({ counter: newCounter })\n        .where(eq(authenticatorsTable.credentialID, credentialID))\n        .returning()\n        .then((res) => res[0])\n\n      if (!authenticator) throw new Error(\"Authenticator not found.\")\n\n      return authenticator as Awaitable<AdapterAuthenticator>\n    },\n  }\n}\n\ntype DefaultSQLiteColumn<\n  T extends {\n    data: string | boolean | number | Date\n    dataType: \"string\" | \"boolean\" | \"number\" | \"date\"\n    notNull: boolean\n    isPrimaryKey?: boolean\n    columnType:\n      | \"SQLiteText\"\n      | \"SQLiteBoolean\"\n      | \"SQLiteTimestamp\"\n      | \"SQLiteInteger\"\n  },","sourceCodeStart":312,"sourceCodeEnd":348,"githubUrl":"https://github.com/nextauthjs/next-auth/blob/a1a16a5a7780488c7449feece410033f445d0b31/packages/adapter-drizzle/src/lib/sqlite.ts#L312-L348","documentation":"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.'","triggerScenarios":"updateAuthenticatorCounter(credentialID, newCounter) where the credentialID is absent from the SQLite authenticators table — credential never registered, deleted, or stored with different encoding.","commonSituations":"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.","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."],"exampleFix":"// before\nawait adapter.updateAuthenticatorCounter(rawCredId, counter)\n// after\nconst credId = toBase64UrlString(rawCredId)\nconst existing = await db.select().from(authenticatorsTable)\n  .where(eq(authenticatorsTable.credentialID, credId))\nif (!existing.length) throw new Error(`credential ${credId} not registered`)\nawait adapter.updateAuthenticatorCounter(credId, counter)","handlingStrategy":"validation","validationCode":"const rows = await db.select().from(authenticatorsTable)\n  .where(eq(authenticatorsTable.credentialID, credentialID))\nif (rows.length === 0) throw new Error(`credentialID ${credentialID} not registered`)\nawait adapter.updateAuthenticatorCounter(credentialID, counter)","typeGuard":null,"tryCatchPattern":"try {\n  await adapter.updateAuthenticatorCounter(credentialID, counter)\n} catch (e) {\n  if ((e as Error).message === 'Authenticator not found.') {\n    console.warn(`Credential ${credentialID} unknown; require re-enrollment`)\n    return\n  }\n  throw e\n}","preventionTips":["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."],"tags":["webauthn","database","drizzle","sqlite","not-found"],"backgroundTag":"record-not-found","analyzedSha":"a1a16a5a7780488c7449feece410033f445d0b31","analyzedAt":"2026-08-28T21:52:38.200Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}