nextauthjs/next-auth · error · Error

userId is undef in createSession

Error message

userId is undef in createSession

What it means

The Neon adapter's createSession guards that userId is defined before inserting into the sessions table, throwing a plain Error otherwise. It protects against writing a NULL 'userId' FK. Receiving undefined here means the caller passed a malformed AdapterSession without a userId.

Source

Thrown at packages/adapter-neon/src/index.ts:172

        account.userId,
        account.provider,
        account.type,
        account.providerAccountId,
        account.access_token,
        account.expires_at,
        account.refresh_token,
        account.id_token,
        account.scope,
        account.session_state,
        account.token_type,
      ]

      const result = await client.query(sql, params)
      return result.rows[0]
    },
    async createSession({ sessionToken, userId, expires }) {
      if (userId === undefined) {
        throw Error(`userId is undef in createSession`)
      }
      const sql = `insert into sessions ("userId", expires, "sessionToken")
      values ($1, $2, $3)
      RETURNING id, "sessionToken", "userId", expires`

      const result = await client.query(sql, [userId, expires, sessionToken])
      return result.rows[0]
    },

    async getSessionAndUser(sessionToken: string | undefined): Promise<{
      session: AdapterSession
      user: AdapterUser
    } | null> {
      if (sessionToken === undefined) {
        return null
      }
      const result1 = await client.query(
        `select * from sessions where "sessionToken" = $1`,

View on GitHub (pinned to a1a16a5a77)

Solutions

  1. Inspect where the session is created and ensure the user record exists with a defined id before createSession is called
  2. Verify your user-mapping function returns the adapter's AdapterUser shape ({ id, ... }) not a raw DB row with different column names
  3. Log the incoming arguments in a wrapper adapter to identify which flow passes userId: undefined
  4. If you have a custom user provider/adapter, confirm createUser returns the inserted row

Example fix

// before
await adapter.createSession({ sessionToken, userId: user.id, expires }) // user.id undefined
// after
if (!user?.id) throw new Error(`Cannot create session: user not persisted (id missing)`)
await adapter.createSession({ sessionToken, userId: user.id, expires })
Defensive patterns

Strategy: type-guard

Validate before calling

if (session?.userId === undefined) throw new Error('createSession requires a persisted user id')

Type guard

function hasUserId(s: { userId?: unknown }): s is { userId: string } {
  return typeof s.userId === 'string' && s.userId.length > 0
}

Try / catch

try {
  await adapter.createSession({ sessionToken, userId, expires })
} catch (e) {
  if (e instanceof Error && e.message === 'userId is undef in createSession') {
    throw new Error('User was not persisted before session creation', { cause: e })
  }
  throw e
}

Prevention

When it happens

Trigger: Calling adapter.createSession({ sessionToken, userId: undefined, expires }) — typically from a session callback/DB session flow where the user row (and thus user.id) was undefined or the createUserServiceUser step silently failed.

Common situations: Custom code constructing session objects manually; a user create step returning undefined (e.g. mapping mismatch in a custom adapter chain); JWT/strategy misconfiguration where sessions are created without a bound user; upstream user id field named differently (e.g. user_id) so id is undefined.

Related errors


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