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
- Inspect the wrapped err.message to find the driver-level cause (connection refused, too many connections, etc.) and address that.
- Increase the connection pool size / database max_connections, or reduce concurrency of write operations.
- Verify the database is reachable and credentials/URL are correct (check pool health and network).
- 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
- Size the connection pool to match DB max_connections.
- Monitor pool wait time and connection errors.
- Keep transactions short to reduce pool pressure.
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
- Error: cannot connect to SQLite: ${message}
- Error: missing MongoDB connection URL.
- Error: cannot connect to MongoDB: ${msg}
- Invalid database type given. Valid types are: ${Object.value
- beginTransaction called while no connection to the database
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/bd7f4d0eaa4c525b.
Report an issue: GitHub.