mongodb/node-mongodb-native · error · MongoTransactionError
No transaction started
Error message
No transaction started
What it means
Thrown by ClientSession.commitTransaction() when the transaction state is NO_TRANSACTION, i.e. startTransaction was never called (or the prior transaction was fully cleared). The driver refuses to send a commit command with no transaction context. It is a MongoTransactionError.
Source
Thrown at src/sessions.ts:431
this.clientOptions?.writeConcern,
readPreference:
options?.readPreference ??
this.defaultTransactionOptions.readPreference ??
this.clientOptions?.readPreference,
maxCommitTimeMS: options?.maxCommitTimeMS ?? this.defaultTransactionOptions.maxCommitTimeMS
});
this.transaction.transition(TxnState.STARTING_TRANSACTION);
}
/**
* Commits the currently active transaction in this session.
*
* @param options - Optional options, can be used to override `defaultTimeoutMS`.
*/
async commitTransaction(options?: { timeoutMS?: number }): Promise<void> {
if (this.transaction.state === TxnState.NO_TRANSACTION) {
throw new MongoTransactionError('No transaction started');
}
if (
this.transaction.state === TxnState.STARTING_TRANSACTION ||
this.transaction.state === TxnState.TRANSACTION_COMMITTED_EMPTY
) {
// the transaction was never started, we can safely exit here
this.transaction.transition(TxnState.TRANSACTION_COMMITTED_EMPTY);
return;
}
if (this.transaction.state === TxnState.TRANSACTION_ABORTED) {
throw new MongoTransactionError(
'Cannot call commitTransaction after calling abortTransaction'
);
}
const command: {View on GitHub (pinned to 3366c21a63)
Solutions
- Gate commit on active state: if (session.inTransaction()) await session.commitTransaction().
- Use withTransaction() to let the driver own commit timing.
- Restructure so commitTransaction is only on the success path of a started transaction.
- After committing/aborting, start a new transaction before committing again.
Example fix
// before
try {
if (needsWrite) session.startTransaction();
await coll.insertOne(doc, { session });
} finally {
await session.commitTransaction(); // throws when needsWrite was false
}
// after
try {
if (needsWrite) {
session.startTransaction();
await coll.insertOne(doc, { session });
await session.commitTransaction();
}
} catch (e) {
if (session.inTransaction()) await session.abortTransaction();
throw e;
} Defensive patterns
Strategy: validation
Validate before calling
if (session.inTransaction()) {
await session.commitTransaction();
} Type guard
function hasActiveTransaction(session: ClientSession): boolean {
return session.inTransaction();
} Try / catch
try {
await session.commitTransaction();
} catch (e) {
if (e instanceof MongoTransactionError && /No transaction started/.test(e.message)) {
// no-op; nothing to commit
} else throw e;
} Prevention
- Gate commitTransaction() behind session.inTransaction().
- Avoid finally blocks that commit unconditionally.
- Use withTransaction() so commit only runs after a successful callback.
When it happens
Trigger: Calling commitTransaction() on a fresh session that never started a transaction; calling commit again after a prior commit/abort cycle reset state to NO_TRANSACTION.
Common situations: A finally block that always calls commitTransaction regardless of whether start ran; conditional start paths where the start branch was skipped but commit runs unconditionally; using the same session across sequential transactions without restarting.
Related errors
- Cannot call commitTransaction after calling abortTransaction
- Cannot call abortTransaction twice
- Cannot call abortTransaction after calling commitTransaction
- Transaction already in progress
- Transactions are not supported in snapshot sessions
AI-assisted analysis of mongodb/node-mongodb-native@3366c21a63 (2026-08-04).
Data as JSON: /data/errors/43a7a647d06594ae.json.
Report an issue: GitHub.