{"record":{"id":"9653dc156bfc3c7a","repo":"mongodb/node-mongodb-native","slug":"cannot-call-aborttransaction-twice","errorCode":null,"errorMessage":"Cannot call abortTransaction twice","messagePattern":"Cannot call abortTransaction twice","errorType":"exception","errorClass":"MongoTransactionError","httpStatus":null,"severity":"error","filePath":"src/sessions.ts","lineNumber":587,"sourceCode":"   *\n   * @param options - Optional options, can be used to override `defaultTimeoutMS`.\n   */\n  async abortTransaction(options?: { timeoutMS?: number }): Promise<void>;\n  /** @internal */\n  async abortTransaction(options?: { timeoutMS?: number; throwTimeout?: true }): Promise<void>;\n  async abortTransaction(options?: { timeoutMS?: number; throwTimeout?: true }): Promise<void> {\n    if (this.transaction.state === TxnState.NO_TRANSACTION) {\n      throw new MongoTransactionError('No transaction started');\n    }\n\n    if (this.transaction.state === TxnState.STARTING_TRANSACTION) {\n      // the transaction was never started, we can safely exit here\n      this.transaction.transition(TxnState.TRANSACTION_ABORTED);\n      return;\n    }\n\n    if (this.transaction.state === TxnState.TRANSACTION_ABORTED) {\n      throw new MongoTransactionError('Cannot call abortTransaction twice');\n    }\n\n    if (\n      this.transaction.state === TxnState.TRANSACTION_COMMITTED ||\n      this.transaction.state === TxnState.TRANSACTION_COMMITTED_EMPTY\n    ) {\n      throw new MongoTransactionError(\n        'Cannot call abortTransaction after calling commitTransaction'\n      );\n    }\n\n    const command: {\n      abortTransaction: 1;\n      writeConcern?: WriteConcernOptions;\n      recoveryToken?: Document;\n    } = { abortTransaction: 1 };\n\n    const timeoutMS =","sourceCodeStart":569,"sourceCodeEnd":605,"githubUrl":"https://github.com/mongodb/node-mongodb-native/blob/dce7939f86fb283e167ad709955abedb7bf23124/src/sessions.ts#L569-L605","documentation":"`abortTransaction` throws MongoTransactionError when the state is already `TRANSACTION_ABORTED` — calling abort twice is rejected because the first abort already ended the transaction.","triggerScenarios":"`session.abortTransaction(); session.abortTransaction();` — typically nested error handlers where both the inner and outer catch attempt to abort.","commonSituations":"A user callback that aborts, then an outer wrapper that aborts again on the propagated error; multiple finally blocks; refactor that duplicated cleanup.","solutions":["Own the abort in exactly one place; do not double-abort.","Guard with `if (session.inTransaction()) await session.abortTransaction();` — once aborted, `inTransaction()` returns false.","Use `withTransaction` to avoid manual abort altogether."],"exampleFix":"// before\nawait session.abortTransaction();\nawait session.abortTransaction(); // throws\n\n// after\nif (session.inTransaction()) {\n  await session.abortTransaction();\n}","handlingStrategy":"validation","validationCode":"function safeAbort(session) {\n  if (session.inTransaction()) return session.abortTransaction();\n}","typeGuard":"function canAbortTxn(session) {\n  return session.inTransaction();\n}","tryCatchPattern":null,"preventionTips":["Abort in exactly one place per transaction attempt.","Guard with `if (session.inTransaction())` since aborted transactions report not-in-progress.","Use `withTransaction` to avoid manual abort bookkeeping."],"tags":["sessions","transactions","validation","state","lifecycle"],"backgroundTag":null,"analyzedSha":"dce7939f86fb283e167ad709955abedb7bf23124","analyzedAt":"2026-08-11T04:54:53.215Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}