nextauthjs/next-auth · error

No user found.

Error message

No user found.

What it means

After issuing UPDATE ... WHERE id = ? RETURNING on the users table, the Drizzle pg adapter checks the returned row. If no row came back, the UPDATE matched zero rows — meaning no user with that id exists — and the adapter throws 'No user found.' rather than silently returning undefined.

Solutions

  1. Verify the user with getUser(id) exists before calling updateUser.
  2. Confirm the adapter's Drizzle client points at the same database/schema where the user row lives (check connection string per environment).
  3. Check whether the row was deleted by another process/job; refresh the id from the source of truth.
  4. Inspect the usersTable schema mapping to make sure eq(usersTable.id, data.id) compares the right column/type.

Example fix

// before
await adapter.updateUser({ id: maybeId, email: newEmail })
// after
const user = await adapter.getUser(maybeId)
if (!user) throw new Error(`User ${maybeId} does not exist`)
await adapter.updateUser({ id: user.id, email: newEmail })
Defensive patterns

Strategy: validation

Validate before calling

const existing = await adapter.getUser(id)
if (!existing) throw new Error(`Cannot update: no user with id ${id}`)
await adapter.updateUser({ id, ...patch })

Type guard

function isExistingUser(u: AdapterUser | null): u is AdapterUser {
  return u !== null && typeof u.id === 'string'
}

Try / catch

try {
  await adapter.updateUser({ id, ...patch })
} catch (e) {
  if ((e as Error).message === 'No user found.') {
    console.warn(`User ${id} not found; skipping update`)
    return null
  }
  throw e
}

Prevention

When it happens

Trigger: Calling updateUser({ id: '<nonexistent-id>' , ...changes }) where the id is not present in the users table (row deleted concurrently, wrong database, stale id from a client).

Common situations: Stale ids held in a client after the user was deleted in another session; pointing the adapter at a different database/env than the one that created the user; soft-delete setups that remove rows the adapter still expects; id type mismatches (string uuid vs integer) that silently match nothing.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at packages/adapter-drizzle/src/lib/pg.ts:203

        .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.")
      }

      const [result] = await client
        .update(usersTable)
        .set(data)
        .where(eq(usersTable.id, data.id))
        .returning()

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

      return result as Awaitable<AdapterUser>
    },
    async updateSession(
      data: Partial<AdapterSession> & Pick<AdapterSession, "sessionToken">
    ) {
      return client
        .update(sessionsTable)
        .set(data)
        .where(eq(sessionsTable.sessionToken, data.sessionToken))
        .returning()
        .then((res) => res[0])
    },
    async linkAccount(data: AdapterAccount) {
      await client.insert(accountsTable).values(data)
    },
    async getUserByAccount(

View on GitHub (pinned to a1a16a5a77)