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
- Ensure your users table uses 'id' as PK or remap rows to AdapterUser ({ id: row.user_id, ... }) in your adapter
- Check that createUser actually inserted and returned the row before createSession runs
- Wrap the adapter and log arguments to find which call path supplies userId: undefined
- 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
- Alias non-'id' PK columns (e.g. user_id) back to id in your row mapper
- Keep the sessions table schema matching Auth.js expectations ('userId' FK NOT NULL)
- Test with the database session strategy in CI
- Never create sessions for requests without an authenticated user row
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
- userId is undef in createSession
- Couldn't create session
- [createSession] Failed to fetch created session
- WebAuthn authenticator not found in database: ${JSON.stringi
- Failed to update authenticator counter. This may cause futur
AI-assisted analysis of nextauthjs/next-auth@a1a16a5a77 (2026-08-28).
Data as JSON: /api/errors/6de6c6ea92ee9a26.
Report an issue: GitHub.