{"record":{"id":"af599726e0497d78","repo":"mongodb/node-mongodb-native","slug":"transaction-already-in-progress","errorCode":null,"errorMessage":"Transaction already in progress","messagePattern":"Transaction already in progress","errorType":"exception","errorClass":"MongoTransactionError","httpStatus":null,"severity":"error","filePath":"src/sessions.ts","lineNumber":394,"sourceCode":"  }\n\n  /**\n   * Starts a new transaction with the given options.\n   *\n   * @remarks\n   * **IMPORTANT**: Running operations in parallel is not supported during a transaction. The use of `Promise.all`,\n   * `Promise.allSettled`, `Promise.race`, etc to parallelize operations inside a transaction is\n   * undefined behaviour.\n   *\n   * @param options - Options for the transaction\n   */\n  startTransaction(options?: TransactionOptions): void {\n    if (this.snapshotEnabled) {\n      throw new MongoCompatibilityError('Transactions are not supported in snapshot sessions');\n    }\n\n    if (this.inTransaction()) {\n      throw new MongoTransactionError('Transaction already in progress');\n    }\n\n    if (this.isPinned && this.transaction.isCommitted) {\n      this.unpin();\n    }\n\n    this.commitAttempted = false;\n    // increment txnNumber\n    this.incrementTransactionNumber();\n    // create transaction state\n    this.transaction = new Transaction({\n      readConcern:\n        options?.readConcern ??\n        this.defaultTransactionOptions.readConcern ??\n        this.clientOptions?.readConcern,\n      writeConcern:\n        options?.writeConcern ??\n        this.defaultTransactionOptions.writeConcern ??","sourceCodeStart":376,"sourceCodeEnd":412,"githubUrl":"https://github.com/mongodb/node-mongodb-native/blob/dce7939f86fb283e167ad709955abedb7bf23124/src/sessions.ts#L376-L412","documentation":"`startTransaction` throws MongoTransactionError when `inTransaction()` is already true — a session hosts at most one transaction at a time. The check protects transaction state from being overwritten mid-flight.","triggerScenarios":"`session.startTransaction(); ... session.startTransaction();` without an intervening commit or abort; control-flow bug where startTransaction is called in a loop body unconditionally.","commonSituations":"Refactoring a sequential flow into a loop and forgetting to commit/abort each iteration; calling withTransaction and also manually calling startTransaction inside the callback; error paths that skip abort.","solutions":["Commit or abort before starting again: `await session.commitTransaction()` or `await session.abortTransaction()`.","Prefer `session.withTransaction(async (s) => { ... })` — it manages start/commit/abort/retry for you.","Always pair start with a finally block: `try { session.startTransaction(); ... await session.commitTransaction(); } catch { await session.abortTransaction(); throw; }`."],"exampleFix":"// before\nsession.startTransaction();\nawait collection.insertOne(doc, { session });\nsession.startTransaction(); // throws\n\n// after\nawait session.withTransaction(async (s) => {\n  await collection.insertOne(doc, { session: s });\n});","handlingStrategy":"validation","validationCode":"function safeStart(session, opts) {\n  if (session.inTransaction()) {\n    throw new Error('transaction already in progress; commit or abort first');\n  }\n  session.startTransaction(opts);\n}","typeGuard":"function canStartTxn(session) {\n  return !session.inTransaction();\n}","tryCatchPattern":null,"preventionTips":["Prefer `withTransaction` to avoid manual start/commit/abort ordering.","Always pair `startTransaction` with commit/abort before starting again.","Add an `if (session.inTransaction())` guard around manual starts."],"tags":["sessions","transactions","validation","state"],"backgroundTag":null,"analyzedSha":"dce7939f86fb283e167ad709955abedb7bf23124","analyzedAt":"2026-08-11T04:54:53.215Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}