nextauthjs/next-auth · error
User id is required
Error message
User id is required
What it means
The SurrealDB adapter's updateUser() requires the partial user object to carry an `id` so it knows which user document to update. If `user.id` is undefined or an empty value, the adapter throws immediately before touching the database. This guards against SurrealDB silently updating nothing (or the wrong record) when the identifier is missing.
Solutions
- Ensure the object passed to updateUser includes a valid `user.id` (the adapter user id, not the raw DB record id)
- Check that id is not being stripped by a sanitizer, DTO mapper, or removeUndefinedFields-like transform before the call
- If updating by email instead, first fetch the user via getUserByEmail and pass the returned full user object
- Log the object right before the call to confirm `id` is present and non-empty
Example fix
// before
await adapter.updateUser({ name: 'New Name' })
// after
const user = await adapter.getUserByEmail('me@example.com')
await adapter.updateUser({ ...user, name: 'New Name' }) Defensive patterns
Strategy: validation
Validate before calling
if (!user?.id) throw new Error('updateUser requires a user with an id')
await adapter.updateUser(user) Type guard
function hasId(u: Partial<AdapterUser>): u is Partial<AdapterUser> & { id: string } {
return typeof u.id === 'string' && u.id.length > 0
} Try / catch
try {
const updated = await adapter.updateUser(user)
} catch (e) {
if (e.message === 'User id is required') {
// fix payload: fetch full user first
}
} Prevention
- Always fetch the full user via getUser/getUserByEmail before partial updates
- Never strip `id` with DTO mappers or spread-based sanitizers before adapter calls
- Add a unit test asserting updateUser rejects/accepts based on id presence
When it happens
Trigger: Calling adapter.updateUser({ name, email }) without an `id` field; building the partial from a form/DTO that drops the id; spreading an object where id was explicitly set to undefined; calling it from custom code rather than through Auth.js flow (which always supplies id).
Common situations: Custom auth scripts that update a user's profile; migrating from another adapter whose updateUser tolerated missing ids; destructuring a session user into a new object and forgetting to copy `id`.
Related errors
- Account not created
- Authenticator not created
- credential id is required
- Unable to update authenticator with credential
- User not updated
AI-assisted analysis of nextauthjs/next-auth@a1a16a5a77 (2026-08-28).
Data as JSON: /api/errors/24049daba511538a.
Report an issue: GitHub.
Appendix: source
Thrown at packages/adapter-surrealdb/src/index.ts:246
provider,
}: Pick<AdapterAccount, "provider" | "providerAccountId">) {
const surreal = await client
try {
const [accounts] = await surreal.query<[AccountDoc<UserDoc>[]]>(
`SELECT userId FROM account WHERE providerAccountId = $providerAccountId AND provider = $provider FETCH userId`,
{
providerAccountId,
provider,
}
)
const user = accounts.at(0)?.userId
if (user) return docToUser(user)
} catch {}
return null
},
async updateUser(user: Partial<AdapterUser>) {
try {
if (!user.id) throw new Error("User id is required")
const surreal = await client
const doc: Partial<UserDoc> | null = removeUndefinedFields(
userToDoc({
...user,
id: undefined,
})
)
if (doc) {
const updatedUser = await surreal.merge<UserDoc, Partial<UserDoc>>(
new RecordId("user", user.id),
doc
)
if (updatedUser) {
return docToUser(updatedUser)
}
}
} catch {}
throw new Error("User not updated")View on GitHub (pinned to a1a16a5a77)