nextauthjs/next-auth · error

Couldn't create session

Error message

Couldn't create session

What it means

createSession inserts a session row and immediately re-selects it by session token in the same batch; if the SELECT yields nothing the adapter throws this error. The insert did not produce a readable session, so no session can be returned to the auth core.

Source

Thrown at packages/adapter-d1/src/index.ts:274

      await deleteRecord(
        db,
        DELETE_ACCOUNT_BY_PROVIDER_AND_PROVIDER_ACCOUNT_ID_SQL,
        [provider, providerAccountId]
      )
    },
    async createSession({ sessionToken, userId, expires }) {
      const id = crypto.randomUUID()
      const createBindings = [id, sessionToken, userId, expires.toISOString()]
      const getBindings = [sessionToken]
      const session = await createRecord<AdapterSession>(
        db,
        CREATE_SESSION_SQL,
        createBindings,
        GET_SESSION_BY_TOKEN_SQL,
        getBindings
      )
      if (session) return session
      throw new Error(`Couldn't create session`)
    },
    async getSessionAndUser(sessionToken) {
      const session: any = await getRecord<AdapterSession>(
        db,
        GET_SESSION_BY_TOKEN_SQL,
        [sessionToken]
      )
      if (session === null) return null

      const user = await getRecord<AdapterUser>(db, GET_USER_BY_ID_SQL, [
        session.userId,
      ])
      if (user === null) return null

      return { session, user }
    },
    async updateSession({ sessionToken, expires }) {
      if (expires === undefined) {

View on GitHub (pinned to a1a16a5a77)

Solutions

  1. Run the adapter's migration SQL so sessions (and accounts/users) tables exist with the expected columns.
  2. Verify the userId passed exists in the users table before creating a session.
  3. Check the D1 binding points at the same database used for users.
  4. Update the adapter if you rely on batch insert+select on local D1/miniflare.

Example fix

// before
await adapter.createSession({ sessionToken, userId: unknownId, expires })
// after
const user = await adapter.getUser(unknownId)
if (!user) throw new Error('cannot create session for missing user')
await adapter.createSession({ sessionToken, userId: unknownId, expires })
Defensive patterns

Strategy: validation

Validate before calling

const user = await adapter.getUser(sessionData.userId)
if (!user) throw new Error('cannot create session: userId does not exist')

Type guard

function isCreateSessionInput(s: unknown): s is AdapterSession {
  return !!s && typeof s === 'object' && typeof (s as AdapterSession).sessionToken === 'string' && (s as AdapterSession).expires instanceof Date
}

Try / catch

try {
  const session = await adapter.createSession({ sessionToken, userId, expires })
} catch (e) {
  if (e instanceof Error && e.message === "Couldn't create session") {
    // check sessions table exists and userId is valid, then retry or re-authenticate
  }
  throw e
}

Prevention

When it happens

Trigger: adapter.createSession({ sessionToken, userId, expires }) when the INSERT fails or the follow-up GET_SESSION_BY_TOKEN_SQL returns no row — sessions table missing, userId referencing a non-existent user, or schema mismatch.

Common situations: Sessions table never migrated, foreign-key mismatch on userId, custom session table names, expired/invalid expires value rejected by constraints, or local miniflare D1 batch quirks.

Related errors


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