nextauthjs/next-auth · error · Error
Unable to update authenticator with credential ${credentialI
Error message
Unable to update authenticator with credential ${credentialId} What it means
The SurrealDB adapter's updateAuthenticator attempts to patch the authenticator record for the given credentialId; any failure (record not found, query error, mapper error) is swallowed by an empty catch and rethrown as this generic Error. It means the WebAuthn authenticator could not be persisted for that credential.
Source
Thrown at packages/adapter-surrealdb/src/index.ts:552
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)
} catch {}
throw Error(
`Unable to update authenticator with credential ${credentialId}`
)
},
}
}
View on GitHub (pinned to a1a16a5a77)
Solutions
- Verify the authenticator record exists in SurrealDB: SELECT * FROM authenticators WHERE credentialId = '...'
- Add logging inside the swallowed catch (or temporarily rethrow) to see the real underlying SurrealDB error
- Check table permissions allow UPDATE for the adapter's user/role
- Ensure the SurrealDB schema matches the adapter's expected authenticators table (counter, credentialPublicKey, credentialDeviceType, etc.)
Example fix
// before
} catch {}
throw Error(`Unable to update authenticator with credential ${credentialId}`)
// after
} catch (e) {
console.error(`updateAuthenticator failed for ${credentialId}`, e)
throw e
} Defensive patterns
Strategy: try-catch
Validate before calling
const existing = await surreal.query(
`SELECT * FROM authenticators WHERE credentialId = $id`, { id: credentialId }
)
if (!existing[0]?.result?.length) throw new Error(`Authenticator ${credentialId} not found`) Type guard
function isAuthenticatorDoc(d: unknown): d is { credentialId: string; counter: number } {
return typeof d === 'object' && d !== null && 'credentialId' in d
} Try / catch
try {
await updateAuthenticator(authenticator)
} catch (e) {
if (e instanceof Error && e.message.includes('Unable to update authenticator')) {
// inspect SurrealDB permissions/schema, then retry or re-register credential
}
throw e
} Prevention
- Keep SurrealDB table permissions open to the adapter's connection user
- Maintain the exact authenticators schema the adapter expects
- Never swallow the underlying catch — patch the adapter or wrap it with logging
- Validate credentials were written by the same adapter version that reads them
When it happens
Trigger: Calling SurrealDBAdapter's updateAuthenticator (invoked via the WebAuthn config flow) when the authenticator row for credentialId does not exist, the credentials table name/structure differs from what the adapter expects, or the SurrealDB UPDATE query fails (permissions, connection).
Common situations: WebAuthn credentials stored by another adapter/version with a different schema; SurrealDB table permissions (DEFINE TABLE ... PERMISSIONS) blocking updates; credentialId casing/format mismatch; SurrealDB instance migration leaving stale authenticator records.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Authenticator not created
- Verification Token not found
- WebAuthn authenticator not found in database: ${JSON.stringi
- Failed to update authenticator counter. This may cause futur
- WebAuthn account not found in database: ${JSON.stringify({cr
AI-assisted analysis of nextauthjs/next-auth@a1a16a5a77 (2026-08-28).
Data as JSON: /api/errors/286c465c5aa8cb20.
Report an issue: GitHub.