payloadcms/payload · critical · APIError

beginTransaction called while no connection to the database

Error message

beginTransaction called while no connection to the database exists

What it means

Thrown by the MongoDB adapter's `beginTransaction` when `this.connection` is falsy — Mongoose has no live connection. Transactions require a session bound to an active connection, so the adapter aborts immediately instead of producing a useless session.

Source

Thrown at packages/db-mongodb/src/transactions/beginTransaction.ts:17

import type { TransactionOptions } from 'mongodb'
import type { BeginTransaction } from 'payload'

import { APIError } from 'payload'
import { v4 as uuid } from 'uuid'

import type { MongooseAdapter } from '../index.js'

// Needs await to fulfill the interface
// @ts-expect-error TransactionOptions isn't compatible with BeginTransaction of the DatabaseAdapter interface.
// eslint-disable-next-line @typescript-eslint/require-await
export const beginTransaction: BeginTransaction = async function beginTransaction(
  this: MongooseAdapter,
  options: TransactionOptions,
) {
  if (!this.connection) {
    throw new APIError('beginTransaction called while no connection to the database exists')
  }

  const client = this.connection.getClient()
  const id = uuid()

  if (!this.sessions[id]) {
    this.sessions[id] = client.startSession()
  }
  if (this.sessions[id]?.inTransaction()) {
    this.payload.logger.warn('beginTransaction called while transaction already exists')
  } else {
    this.sessions[id]?.startTransaction(options || (this.transactionOptions as TransactionOptions))
  }

  return id
}

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Await `payload.init()` (and the underlying connect) before serving any request.
  2. Verify the MongoDB URI/host is reachable and credentials are valid.
  3. Guard with `adapter.connection && adapter.connection.readyState === 1` before starting a transaction.
  4. In serverless, reuse the connection across invocations and do not close it between calls.

Example fix

// before
app.post('/x', handler) // starts before DB is up
// after
await payload.init()
app.post('/x', handler)
Defensive patterns

Strategy: validation

Validate before calling

function assertConnected(adapter) {
  if (!adapter.connection || adapter.connection.readyState !== 1)
    throw new Error('MongoDB connection not ready')
}

Type guard

const isMongoConnected = (adapter) =>
  Boolean(adapter.connection) && adapter.connection.readyState === 1

Try / catch

try { await payload.db.beginTransaction() }
catch (e) { if (/no connection to the database/.test(e.message)) await waitForDb(payload) else throw e }

Prevention

When it happens

Trigger: Any transactional operation (payload.db.beginTransaction, or any op wrapped in a transaction) that runs before `payload.init()`/`connect` resolves, after the connection was closed/dropped, or when the initial connect failed but the app kept serving requests.

Common situations: Handling requests during startup before the init promise resolves; MongoDB unreachable at boot but the process continues; calling `mongoose.disconnect()`/`db.close()` then querying; serverless cold-start races where the connection isn't established before the first transactional request.

Related errors


AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12). Data as JSON: /api/errors/9a2ca1237cf612dc. Report an issue: GitHub.