sequelize/sequelize · error · Error

Unable to create a savepoint without the transaction object.

Error message

Unable to create a savepoint without the transaction object.

What it means

Thrown by MsSqlQueryInterfaceTypescript._createSavepoint (packages/mssql/src/query-interface-typescript.internal.ts:47) when the parent transaction argument is missing or not a Transaction instance. Savepoints in MSSQL are issued through the parent transaction's connection, so Sequelize must hold a valid Transaction to call tedious's saveTransaction.

Source

Thrown at packages/mssql/src/query-interface-typescript.internal.ts:47

    transaction: Transaction,
    _options: CommitTransactionOptions,
  ): Promise<void> {
    if (!transaction || !(transaction instanceof Transaction)) {
      throw new Error('Unable to commit a transaction without the transaction object.');
    }

    const connection = transaction.getConnection() as MsSqlConnection;
    await connection[ASYNC_QUEUE].enqueue(
      async () =>
        new Promise<void>((resolve, reject) => {
          connection.commitTransaction(error => (error ? reject(error) : resolve()));
        }),
    );
  }

  async _createSavepoint(transaction: Transaction, options: CreateSavepointOptions): Promise<void> {
    if (!transaction || !(transaction instanceof Transaction)) {
      throw new Error('Unable to create a savepoint without the transaction object.');
    }

    const connection = transaction.getConnection() as MsSqlConnection;
    await connection[ASYNC_QUEUE].enqueue(
      async () =>
        new Promise<void>((resolve, reject) => {
          connection.saveTransaction(
            error => (error ? reject(error) : resolve()),
            options.savepointName,
          );
        }),
    );
  }

  async _rollbackSavepoint(
    transaction: Transaction,
    options: RollbackSavepointOptions,
  ): Promise<void> {

View on GitHub (pinned to 7e1deec499)

Solutions

  1. Always thread the parent transaction through to the savepoint call.
  2. Use the managed nested API: sequelize.transaction(async t => { await sequelize.transaction({ transaction: t }, async () => { ... }); }).
  3. Validate that the transaction is still active (t.finished === undefined) before creating a savepoint.
  4. Avoid reusing a transaction variable after it has been committed/rolled back.

Example fix

// before
await queryInterface._createSavepoint(undefined, { savepointName: 'sp1' }); // throws

// after
const t = await sequelize.transaction();
await sequelize.transaction({ transaction: t }, async () => { /* savepoint scope */ });
Defensive patterns

Strategy: type-guard

Validate before calling

import { Transaction } from '@sequelize/core';
if (!(parent instanceof Transaction)) throw new Error('savepoint requires a parent Transaction');
await sequelize.transaction({ transaction: parent }, async () => { /* ... */ });

Type guard

import { Transaction } from '@sequelize/core';
function isActiveTransaction(value: unknown): value is Transaction {
  return value instanceof Transaction && value.finished === undefined;
}

Try / catch

await sequelize.transaction({ transaction: parent }, async () => { await work(); }); // auto savepoint+rollback

Prevention

When it happens

Trigger: Calling transaction.savePoint / _createSavepoint without passing the enclosing transaction, or passing a transaction that was already committed/rolled back and discarded.

Common situations: Building nested transactions manually and losing the parent reference; wrapping a helper that forgets to forward the transaction parameter; using an ORM helper that accepts an optional transaction which arrives as undefined.

Related errors


AI-assisted analysis of sequelize/sequelize@7e1deec499 (2026-08-03). Data as JSON: /data/errors/97e574dc23697aab.json. Report an issue: GitHub.