ruvnet/ruflo · error
Transaction is not active. Call begin() first.
Error message
Transaction is not active. Call begin() first.
What it means
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.
Source
Thrown at v3/@claude-flow/plugins/src/integrations/ruvector/streaming.ts:1204
savepoints: string[];
queryCount: number;
durationMs: number;
} {
return {
transactionId: this.transactionId,
isActive: this.isActive,
savepoints: Array.from(this.savepoints),
queryCount: this.queryCount,
durationMs: this.startTime ? Date.now() - this.startTime : 0,
};
}
/**
* Ensure transaction is active.
*/
private ensureActive(): void {
if (!this.isActive) {
throw new Error('Transaction is not active. Call begin() first.');
}
}
/**
* Build search query SQL.
*/
private buildSearchQuery(options: VectorSearchOptions): { sql: string; params: unknown[] } {
const tableName = options.tableName ?? this.defaultTableName;
const vectorColumn = options.vectorColumn ?? 'embedding';
const metric = options.metric ?? 'cosine';
const operator = DISTANCE_OPERATORS[metric] ?? '<=>';
const queryVector = this.formatVector(options.query);
const schemaPrefix = this.schema ? `${this.escapeIdentifier(this.schema)}.` : '';
const selectColumns = options.selectColumns ?? ['id'];
const columnList = [...selectColumns];
View on GitHub (pinned to fa13ee4ad6)
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
Example fix
// before
const tx = await client.transaction();
await tx.insert({ vectors }); // throws: Transaction is not active
await tx.commit();
// after
const tx = await client.transaction();
await tx.begin();
try {
await tx.insert({ vectors });
await tx.commit();
} catch (err) {
await tx.rollback();
throw err;
} Defensive patterns
Strategy: validation
Validate before calling
// Check transaction liveness via the public status API before operating
const status = tx.getStatus();
if (!status.isActive) {
await tx.begin();
}
await tx.insert({ vectors }); Type guard
function isActiveTransaction(tx: { getStatus(): { isActive: boolean } }): boolean {
return tx.getStatus().isActive;
} Try / catch
try {
await tx.query(sql);
} catch (err) {
if (err instanceof Error && err.message.includes('Transaction is not active')) {
// restart the unit of work on a fresh transaction
await tx.begin();
return tx.query(sql);
}
throw err;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Can only resume paused agent
- Batch ${batchIndex} failed after ${attempt} attempts: ${last
- Cannot start terminated agent
- Can only pause active or busy agent
- Can only recover from error state
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/adec7563ae07b14a.
Report an issue: GitHub.