nextauthjs/next-auth · error · Error

userId is undef in createSession

Error message

userId is undef in createSession

What it means

Same guard as the Neon adapter: the pg adapter's createSession throws when userId is undefined to prevent inserting a NULL 'userId' foreign key into the sessions table. It surfaces a malformed AdapterSession argument rather than a SQL constraint error.

Source

Thrown at packages/adapter-pg/src/index.ts:180

        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 mapExpiresAt(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. Ensure your users table uses 'id' as PK or remap rows to AdapterUser ({ id: row.user_id, ... }) in your adapter
  2. Check that createUser actually inserted and returned the row before createSession runs
  3. Wrap the adapter and log arguments to find which call path supplies userId: undefined
  4. Regenerate/verify the pg schema matches the Auth.js expected sessions table ('userId' FK NOT NULL)

Example fix

// before
return rows[0] // { user_id: 5, ... }
// after
return { id: row.user_id, email: row.email, emailVerified: row.email_verified, ...row }
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('Session created without a persisted user (pg adapter)', { cause: e })
  }
  throw e
}

Prevention

When it happens

Trigger: Calling createSession({ sessionToken, userId: undefined, expires }) on the pg adapter — usually because the preceding createUser/user lookup returned an object whose id field is undefined (column-name mismatch like user_id, or a failed insert).

Common situations: Custom pg schemas where the users table PK column differs from 'id' and the row isn't remapped; drizzle/knex mappers returning raw rows; database session strategy creating sessions for anonymous requests; adapter version drift where mapping functions changed.

Related errors


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