payloadcms/payload · critical · Error

Error: cannot begin transaction: ${err.message}

Error message

Error: cannot begin transaction: ${err.message}

What it means

Thrown by beginTransaction when the underlying Drizzle/driver call to open a transaction fails. The adapter awaits a readiness promise that is rejected by the driver's .catch handler, and the thrown error wraps the original driver message. Root causes are infrastructure-level: connection pool exhaustion, the DB refusing connections, a dead server, or a driver bug.

Source

Thrown at packages/drizzle/src/transactions/beginTransaction.ts:67

        // Connection failed before callback ran - reject instead of hanging forever
        transactionFailed(err)
      })

    // Need to wait until the transaction is ready
    // before binding its `resolve` and `reject` methods below
    await new Promise<void>((res, rej) => {
      transactionReady = res
      transactionFailed = rej
    })

    this.sessions[id] = {
      db: transaction,
      reject,
      resolve,
    }
  } catch (err) {
    this.payload.logger.error({ err, msg: `Error: cannot begin transaction: ${err.message}` })
    throw new Error(`Error: cannot begin transaction: ${err.message}`)
  }

  return id
}

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Inspect the wrapped err.message to find the driver-level cause (connection refused, too many connections, etc.) and address that.
  2. Increase the connection pool size / database max_connections, or reduce concurrency of write operations.
  3. Verify the database is reachable and credentials/URL are correct (check pool health and network).
  4. Retry the request after a brief backoff for transient connection failures.
Defensive patterns

Strategy: retry

Validate before calling

async function checkPool(db) {
  // run a trivial query before issuing transactional writes
  await db.execute('SELECT 1')
}

Try / catch

try {
  await payload.create({ collection, data })
} catch (err) {
  if (/cannot begin transaction/.test(err.message)) {
    // backoff and retry once; surface persistent failures to ops
  } else throw err
}

Prevention

When it happens

Trigger: Any Payload write operation that runs inside a transaction (create/update/delete with default transaction behavior) at a moment when the DB cannot start a transaction — pool full, DB down, network partition, max_connections reached.

Common situations: DB under heavy load; misconfigured pool size; DB restarted while the app held idle connections; running migrations against an unreachable database URL.

Related errors


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