{"record":{"id":"cabf564f8202c819","repo":"ruvnet/ruflo","slug":"transaction-already-active","errorCode":null,"errorMessage":"Transaction already active","messagePattern":"Transaction already active","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/plugins/src/integrations/ruvector/streaming.ts","lineNumber":860,"sourceCode":"  ) {\n    super();\n    this.client = client;\n    this.schema = options.schema;\n    this.defaultTableName = options.defaultTableName ?? 'vectors';\n  }\n\n  // ===========================================================================\n  // Transaction Control\n  // ===========================================================================\n\n  /**\n   * Begin a transaction with optional isolation level.\n   *\n   * @param isolation - Transaction isolation level\n   */\n  async begin(isolation?: IsolationLevel): Promise<void> {\n    if (this.isActive) {\n      throw new Error('Transaction already active');\n    }\n\n    this.transactionId = `tx_${Date.now()}_${Math.random().toString(36).slice(2)}`;\n    this.startTime = Date.now();\n\n    let sql = 'BEGIN';\n    if (isolation) {\n      sql += ` ISOLATION LEVEL ${isolation.replace('_', ' ').toUpperCase()}`;\n    }\n\n    await this.client.query(sql);\n    this.isActive = true;\n    this.queryCount = 1;\n\n    this.emit('begin', { transactionId: this.transactionId, isolation });\n  }\n\n  /**","sourceCodeStart":842,"sourceCodeEnd":878,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/plugins/src/integrations/ruvector/streaming.ts#L842-L878","documentation":"Thrown by the transaction wrapper's begin() when this.isActive is already true. The wrapper binds one pg client to one transaction lifecycle; issuing BEGIN twice on the same client without an intervening commit/rollback would nest transactions illegally in Postgres, so the second begin() is rejected.","triggerScenarios":"A helper and its caller both calling begin() on the same transaction object; reusing a transaction instance across loop iterations without commit/rollback; retry logic that re-issues begin() after a statement error while the transaction is still open.","commonSituations":"Extracting a 'withTransaction' utility that begins internally while callers also begin; batch ingestion loops reusing one transaction for multiple batches; error paths that abort before rollback and then retry begin().","solutions":["Commit or rollback the active transaction before starting another on the same object","Create a new transaction object per unit of work instead of reusing instances","Guard with the wrapper's isActive (or a hasActiveTransaction helper) before calling begin()"],"exampleFix":"// before\nawait tx.begin();\nawait tx.begin(); // throws\n\n// after\nawait tx.begin();\nawait tx.commit();\nawait tx.begin();","handlingStrategy":"validation","validationCode":"if (tx.isActive) {\n  await tx.rollback(); // or commit, depending on outcome\n}\nawait tx.begin(isolation);","typeGuard":"const hasActiveTransaction = (t: { isActive: boolean }): boolean =>\n  t.isActive === true;","tryCatchPattern":"try {\n  await tx.begin();\n} catch (err) {\n  if (err instanceof Error && err.message === 'Transaction already active') {\n    await tx.rollback(); // clean slate, then retry\n    await tx.begin();\n  } else {\n    throw err;\n  }\n}","preventionTips":["Use one transaction object per unit of work; create fresh rather than reusing","Always pair begin() with commit()/rollback() in try/finally so isActive cannot leak true","Keep begin() in exactly one layer (the withTransaction helper), never in both helper and caller"],"tags":["database","transactions","state-validation","postgres"],"backgroundTag":"invalid-state-transition","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}