{"record":{"id":"adec7563ae07b14a","repo":"ruvnet/ruflo","slug":"transaction-is-not-active-call-begin-first","errorCode":null,"errorMessage":"Transaction is not active. Call begin() first.","messagePattern":"Transaction is not active\\. Call begin\\(\\) first\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/plugins/src/integrations/ruvector/streaming.ts","lineNumber":1204,"sourceCode":"    savepoints: string[];\n    queryCount: number;\n    durationMs: number;\n  } {\n    return {\n      transactionId: this.transactionId,\n      isActive: this.isActive,\n      savepoints: Array.from(this.savepoints),\n      queryCount: this.queryCount,\n      durationMs: this.startTime ? Date.now() - this.startTime : 0,\n    };\n  }\n\n  /**\n   * Ensure transaction is active.\n   */\n  private ensureActive(): void {\n    if (!this.isActive) {\n      throw new Error('Transaction is not active. Call begin() first.');\n    }\n  }\n\n  /**\n   * Build search query SQL.\n   */\n  private buildSearchQuery(options: VectorSearchOptions): { sql: string; params: unknown[] } {\n    const tableName = options.tableName ?? this.defaultTableName;\n    const vectorColumn = options.vectorColumn ?? 'embedding';\n    const metric = options.metric ?? 'cosine';\n    const operator = DISTANCE_OPERATORS[metric] ?? '<=>';\n\n    const queryVector = this.formatVector(options.query);\n    const schemaPrefix = this.schema ? `${this.escapeIdentifier(this.schema)}.` : '';\n\n    const selectColumns = options.selectColumns ?? ['id'];\n    const columnList = [...selectColumns];\n","sourceCodeStart":1186,"sourceCodeEnd":1222,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/plugins/src/integrations/ruvector/streaming.ts#L1186-L1222","documentation":"Thrown by StreamingVectorClient's transaction wrapper (ruvector streaming.ts) from the private ensureActive() guard. Every transactional method (insert, update, delete, query) calls ensureActive() first, which checks the isActive flag set only by begin(). If the transaction was never started, or was already committed/rolled back, any further operation throws this error.","triggerScenarios":"Calling tx.insert()/update()/delete()/query() on a transaction object without awaiting tx.begin() first; reusing the same transaction object after commit() or rollback() (isActive flips back to false); a begin() that failed (e.g. connection error) whose error was swallowed, then proceeding to query.","commonSituations":"Refactoring code that used the non-transactional client API into transactions and forgetting the begin() call; retry logic that reruns the transaction body but reuses the spent transaction object; early commit/rollback inside a loop followed by more statements; missing await on begin() so subsequent statements run before the flag is set.","solutions":["Await tx.begin() before any other call on the transaction object","If the error appears mid-flow, check whether commit() or rollback() was already called earlier (e.g. in an early-return branch) and get a fresh transaction instead of reusing the object","Wrap the whole unit of work in a helper that always begins and always commits/rollbacks, so begin() cannot be skipped","Inspect tx.getStatus().isActive after begin() when the connection is flaky, to confirm the transaction really started"],"exampleFix":"// before\nconst tx = await client.transaction();\nawait tx.insert({ vectors }); // throws: Transaction is not active\nawait tx.commit();\n\n// after\nconst tx = await client.transaction();\nawait tx.begin();\ntry {\n  await tx.insert({ vectors });\n  await tx.commit();\n} catch (err) {\n  await tx.rollback();\n  throw err;\n}","handlingStrategy":"validation","validationCode":"// Check transaction liveness via the public status API before operating\nconst status = tx.getStatus();\nif (!status.isActive) {\n  await tx.begin();\n}\nawait tx.insert({ vectors });","typeGuard":"function isActiveTransaction(tx: { getStatus(): { isActive: boolean } }): boolean {\n  return tx.getStatus().isActive;\n}","tryCatchPattern":"try {\n  await tx.query(sql);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('Transaction is not active')) {\n    // restart the unit of work on a fresh transaction\n    await tx.begin();\n    return tx.query(sql);\n  }\n  throw err;\n}","preventionTips":["Always pair transaction acquisition with begin() in the same function so they cannot be separated","Never reuse a transaction object after commit() or rollback(); create a new one per unit of work","Wrap begin/work/commit in a single helper with try/catch that rollbacks on failure","Await every transaction call - a floating begin() promise lets statements race the isActive flag"],"tags":["transaction","state-machine","vector-database","typescript"],"backgroundTag":"transaction-not-started","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}