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

  1. Ensure the object passed to updateUser includes a valid `user.id` (the adapter user id, not the raw DB record id)
  2. Check that id is not being stripped by a sanitizer, DTO mapper, or removeUndefinedFields-like transform before the call
  3. If updating by email instead, first fetch the user via getUserByEmail and pass the returned full user object
  4. 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

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


AI-assisted analysis of nextauthjs/next-auth@a1a16a5a77 (2026-08-28). Data as JSON: /api/errors/24049daba511538a. Report an issue: GitHub.