{"record":{"id":"032396f48f754a27","repo":"nextauthjs/next-auth","slug":"credential-id-is-required","errorCode":null,"errorMessage":"credential id is required","messagePattern":"credential id is required","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/adapter-surrealdb/src/index.ts","lineNumber":539,"sourceCode":"      const surreal = await client\n      try {\n        const [authenticatorDocs] = await surreal.query<[AuthenticatorDoc[]]>(\n          `SELECT * FROM authenticator WHERE userId = $userId LIMIT 1`,\n          {\n            userId,\n          }\n        )\n\n        return authenticatorDocs.map((v) => docToAuthenticator(v))\n      } catch {}\n      throw new Error(\"Verification Token not found\")\n    },\n    async updateAuthenticatorCounter(\n      credentialId: AdapterAuthenticator[\"credentialID\"],\n      newCounter: AdapterAuthenticator[\"counter\"]\n    ) {\n      try {\n        if (!credentialId) throw new Error(\"credential id is required\")\n        const surreal = await client\n        const [authenticatorDoc] = await surreal.query<[AuthenticatorDoc]>(\n          `UPDATE ONLY authenticator MERGE $doc WHERE credentialID = $cid`,\n          {\n            cid: credentialId,\n            doc: {\n              counter: newCounter,\n            },\n          }\n        )\n        return docToAuthenticator(authenticatorDoc)\n      } catch {}\n      throw Error(\n        `Unable to update authenticator with credential ${credentialId}`\n      )\n    },\n  }\n}","sourceCodeStart":521,"sourceCodeEnd":557,"githubUrl":"https://github.com/nextauthjs/next-auth/blob/a1a16a5a7780488c7449feece410033f445d0b31/packages/adapter-surrealdb/src/index.ts#L521-L557","documentation":"updateAuthenticatorCounter() bumps a WebAuthn credential's counter after successful authentication. It validates upfront that credentialId is truthy and throws 'credential id is required' otherwise, preventing a SurrealDB UPDATE with no WHERE target.","triggerScenarios":"Calling updateAuthenticatorCounter(undefined/null/'', counter); credentialID lost when deserializing the WebAuthn credential (e.g. base64url decoding produced undefined); custom code driving the adapter directly.","commonSituations":"Passkey authentication where the credentialID from the browser assertion wasn't converted back to the stored string form; building the call from a request body missing the credential id field.","solutions":["Pass the exact credentialID string stored in the authenticator document","Check base64url encode/decode of credentialID between browser and server is symmetric","Guard the call site: only invoke when the authenticator record was actually fetched","Log credentialId before the call to confirm it is non-empty"],"exampleFix":"// before\nawait adapter.updateAuthenticatorCounter(credential.id, credential.counter)\n// after\nif (!credential?.id) throw new Error('Missing credentialID from assertion')\nawait adapter.updateAuthenticatorCounter(credential.id, credential.counter)","handlingStrategy":"validation","validationCode":"if (typeof credentialId !== 'string' || credentialId.length === 0) {\n  throw new Error('Cannot update counter: credentialId missing')\n}\nawait adapter.updateAuthenticatorCounter(credentialId, newCounter)","typeGuard":"function hasCredentialId(id: unknown): id is string {\n  return typeof id === 'string' && id.length > 0\n}","tryCatchPattern":"try {\n  await adapter.updateAuthenticatorCounter(credentialId, counter)\n} catch (e) {\n  if (e.message === 'credential id is required') {\n    console.error('Assertion credentialID lost during decode — check base64url handling')\n  }\n  throw e\n}","preventionTips":["Use symmetric base64url encode/decode for credentialID between browser and server","Never pass request-body fields to the adapter without validating presence","Preserve credentialID when deserializing WebAuthn assertion results"],"tags":["adapter","webauthn","validation"],"backgroundTag":"missing-required-identifier","analyzedSha":"a1a16a5a7780488c7449feece410033f445d0b31","analyzedAt":"2026-08-28T21:52:38.200Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}