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.
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)
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
- User not updated
- Account not created
- Verification Token not created
- Verification Token not used
- Authenticator not created
AI-assisted analysis of nextauthjs/next-auth@a1a16a5a77 (2026-08-28).
Data as JSON: /api/errors/24049daba511538a.
Report an issue: GitHub.