{"record":{"id":"ed4fc7a1a5a555d8","repo":"nextauthjs/next-auth","slug":"authenticator-not-found-ed4fc7","errorCode":null,"errorMessage":"Authenticator not found.","messagePattern":"Authenticator not found\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/adapter-drizzle/src/lib/pg.ts","lineNumber":322,"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 DefaultPostgresColumn<\n  T extends {\n    data: string | number | boolean | Date\n    dataType: \"string\" | \"number\" | \"boolean\" | \"date\"\n    notNull: boolean\n    isPrimaryKey?: boolean\n    columnType:\n      | \"PgVarchar\"\n      | \"PgText\"\n      | \"PgBoolean\"\n      | \"PgTimestamp\"\n      | \"PgInteger\"","sourceCodeStart":304,"sourceCodeEnd":340,"githubUrl":"https://github.com/nextauthjs/next-auth/blob/a1a16a5a7780488c7449feece410033f445d0b31/packages/adapter-drizzle/src/lib/pg.ts#L304-L340","documentation":"updateAuthenticatorCounter updates a WebAuthn authenticator's counter by credentialID and returns the updated row. When the UPDATE ... RETURNING finds no authenticator with that credentialID, the adapter throws 'Authenticator not found.' because there is nothing to update.","triggerScenarios":"Calling updateAuthenticatorCounter(credentialID, newCounter) with a credentialID that is not stored in the authenticators table — e.g. the credential was deleted, registered in a different database, or the ID string differs in encoding.","commonSituations":"WebAuthn flows where the authenticator was removed server-side but the browser still presents the credential; running against a fresh database without prior authenticator registration; base64url vs hex encoding mismatches of credentialID between client and DB.","solutions":["Confirm the credentialID exists by querying the authenticators table (or listUserAuthenticators) before updating the counter.","Re-register the WebAuthn credential if it was deleted; then retry the counter update.","Check credentialID encoding consistency (base64url vs base64 vs hex) between client, @simplewebauthn, and the stored column value.","Verify the adapter is connected to the same database used during registration."],"exampleFix":"// before\nawait adapter.updateAuthenticatorCounter(credId, counter)\n// after\nconst auth = await adapter.listAuthenticatorsByUserId(userId)\nif (!auth.some(a => a.credentialID === credId)) {\n  throw new Error(`Unknown credentialID ${credId}`)\n}\nawait adapter.updateAuthenticatorCounter(credId, counter)","handlingStrategy":"validation","validationCode":"const known = await adapter.listAuthenticatorsByUserId(userId)\nif (!known.some(a => a.credentialID === credentialID)) {\n  throw new Error(`credentialID ${credentialID} not registered`)\n}\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(`Unknown credential ${credentialID}; forcing re-registration`)\n    return\n  }\n  throw e\n}","preventionTips":["Normalize credentialID encoding (base64url string) everywhere before storing/comparing.","Delete server-side authenticator records whenever a credential is removed client-side.","Use one database per environment so registrations and counter updates land in the same store."],"tags":["webauthn","database","drizzle","postgresql","not-found"],"backgroundTag":"record-not-found","analyzedSha":"a1a16a5a7780488c7449feece410033f445d0b31","analyzedAt":"2026-08-28T21:52:38.200Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}