ruvnet/ruflo · error

Savepoint '${name}' does not exist

Error message

Savepoint '${name}' does not exist

What it means

Thrown by rollbackToSavepoint() when the given name is not in the wrapper's savepoints map. Savepoints must be created via savepoint(name) on the same active transaction before they can be rolled back to; the map (not the DB catalog) is the source of truth, so names created elsewhere, released already, or misspelled all fail.

Source

Thrown at v3/@claude-flow/plugins/src/integrations/ruvector/streaming.ts:903

    const escapedName = this.escapeIdentifier(name);
    await this.client.query(`SAVEPOINT ${escapedName}`);
    this.savepoints.add(name);
    this.queryCount++;

    this.emit('savepoint', { transactionId: this.transactionId, name });
  }

  /**
   * Rollback to a savepoint.
   *
   * @param name - Savepoint name
   */
  async rollbackToSavepoint(name: string): Promise<void> {
    this.ensureActive();

    if (!this.savepoints.has(name)) {
      throw new Error(`Savepoint '${name}' does not exist`);
    }

    const escapedName = this.escapeIdentifier(name);
    await this.client.query(`ROLLBACK TO SAVEPOINT ${escapedName}`);
    this.queryCount++;

    this.emit('rollback_to_savepoint', { transactionId: this.transactionId, name });
  }

  /**
   * Release a savepoint.
   *
   * @param name - Savepoint name
   */
  async releaseSavepoint(name: string): Promise<void> {
    this.ensureActive();

    if (!this.savepoints.has(name)) {

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Create the savepoint first: await tx.savepoint('sp1') before any rollbackToSavepoint('sp1')
  2. Remember that releaseSavepoint removes the name — do not roll back to a released savepoint; re-create it if needed
  3. Derive names from constants or a single helper so creation and rollback sites cannot diverge

Example fix

// before
await tx.begin();
await tx.rollbackToSavepoint('sp1'); // throws

// after
await tx.begin();
await tx.savepoint('sp1');
// ... work ...
await tx.rollbackToSavepoint('sp1');
Defensive patterns

Strategy: validation

Validate before calling

if (!hasSavepoint(tx, 'sp1')) {
  await tx.savepoint('sp1');
}
// ... risky work ...
await tx.rollbackToSavepoint('sp1');

Type guard

type SavepointCapable = { savepoints: Map<string, unknown> };
const hasSavepoint = (t: SavepointCapable, name: string): boolean =>
  t.savepoints.has(name);

Try / catch

try {
  await tx.rollbackToSavepoint(name);
} catch (err) {
  if (err instanceof Error && err.message.includes("does not exist")) {
    await tx.savepoint(name); // recreate and continue, or surface a logic error
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: Calling rollbackToSavepoint('sp1') without a prior savepoint('sp1'); rolling back to a savepoint after releaseSavepoint already deleted it from the map; name drift between creation ('sp_1') and rollback ('sp1').

Common situations: Nested-workflow code where the create and rollback sites are far apart or written by different authors; generated names that differ across retries; refactors that renamed savepoints on one path only.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/234600553a716d28. Report an issue: GitHub.