nextauthjs/next-auth · error

No user id.

Error message

No user id.

What it means

The MySQL drizzle adapter's updateUser requires data.id to locate the row to update and throws this error when id is missing. updateUser in Auth.js always supplies an id, so this usually means updateUser was called directly with a partial user object lacking id.

Source

Thrown at packages/adapter-drizzle/src/lib/mysql.ts:213

        .then((res) => res[0])
    },
    async getSessionAndUser(sessionToken: string) {
      return client
        .select({
          session: sessionsTable,
          user: usersTable,
        })
        .from(sessionsTable)
        .where(eq(sessionsTable.sessionToken, sessionToken))
        .innerJoin(usersTable, eq(usersTable.id, sessionsTable.userId))
        .then((res) => (res.length > 0 ? res[0] : null)) as Awaitable<{
        session: AdapterSession
        user: AdapterUser
      } | null>
    },
    async updateUser(data: Partial<AdapterUser> & Pick<AdapterUser, "id">) {
      if (!data.id) {
        throw new Error("No user id.")
      }

      await client
        .update(usersTable)
        .set(data)
        .where(eq(usersTable.id, data.id))

      const [result] = await client
        .select()
        .from(usersTable)
        .where(eq(usersTable.id, data.id))

      if (!result) {
        throw new Error("No user found.")
      }

      return result as Awaitable<AdapterUser>
    },

View on GitHub (pinned to a1a16a5a77)

Solutions

  1. Always include id: adapter.updateUser({ id, ...changes }).
  2. Validate the id exists before calling updateUser.
  3. If updating by email, first getUserByEmail then use the returned user's id.

Example fix

// before
await adapter.updateUser({ email: 'new@mail.com' })
// after
const user = await adapter.getUserByEmail('new@mail.com')
await adapter.updateUser({ id: user.id, email: 'new@mail.com' })
Defensive patterns

Strategy: validation

Validate before calling

if (!data.id) throw new Error('updateUser requires an id')

Type guard

function hasId(u: Partial<AdapterUser>): u is Partial<AdapterUser> & Pick<AdapterUser, 'id'> {
  return typeof u.id === 'string' && u.id.length > 0
}

Try / catch

try {
  await adapter.updateUser(data)
} catch (e) {
  if (e instanceof Error && e.message === 'No user id.') {
    // resolve the id via getUserByEmail/getUserByAccount, then retry
  }
  throw e
}

Prevention

When it happens

Trigger: Calling adapter.updateUser({ email: 'x' }) without an id property, or id present but undefined due to destructuring/spread of a partial object.

Common situations: Custom code calling the adapter directly, building the update object dynamically and dropping id, or integrating with a framework that passes Partial<AdapterUser>.

Understand the failure class

Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.

Related errors


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