{"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/3366c21a6311e02f1be91da982f9b93d3cce99a0/src/sessions.ts#L376-L412","documentation":"Thrown by ClientSession.startTransaction() when a transaction is already active on the session (state is STARTING_TRANSACTION or TRANSACTION_IN_PROGRESS). The driver enforces one active transaction per session per the sessions spec. It is a MongoTransactionError.","triggerScenarios":"Two consecutive calls to session.startTransaction() with no intervening commitTransaction/abortTransaction; a re-entrant helper that starts a transaction while the caller already did.","commonSituations":"Refactoring code so a transaction is started both outside and inside a function; retry loops that restart a transaction without aborting the previous one; copy-paste of startTransaction boilerplate.","solutions":["Commit or abort the existing transaction before starting a new one: await session.abortTransaction() then session.startTransaction().","Check state first: if (!session.inTransaction()) session.startTransaction().","Use withTransaction() which manages start/commit/abort lifecycle internally instead of calling startTransaction yourself.","Audit helper functions to ensure only one layer owns transaction begin/commit."],"exampleFix":"// before\nsession.startTransaction();\n// ... later, branch re-enters\nsession.startTransaction(); // throws\n\n// after\nif (!session.inTransaction()) {\n  session.startTransaction();\n}","handlingStrategy":"validation","validationCode":"if (!session.inTransaction()) {\n  session.startTransaction();\n}","typeGuard":"function isTransactionFree(session: ClientSession): boolean {\n  return !session.inTransaction();\n}","tryCatchPattern":"try {\n  session.startTransaction();\n} catch (e) {\n  if (e instanceof MongoTransactionError && /already in progress/.test(e.message)) {\n    // abort or commit the existing one first\n    await session.abortTransaction();\n    session.startTransaction();\n  } else throw e;\n}","preventionTips":["Always check session.inTransaction() before calling startTransaction().","Prefer withTransaction() over manual start/commit/abort.","Ensure only one layer of your code owns transaction begin."],"tags":["sessions","transactions","state-machine","invalid-argument"],"analyzedSha":"3366c21a6311e02f1be91da982f9b93d3cce99a0","analyzedAt":"2026-08-04T13:40:15.335Z","schemaVersion":2}