dotnet/efcore · error · NotSupportedException

User transaction is not supported with a…

Error message

User transaction is not supported with a TransactionSuppressed migrations or a retrying execution strategy.

What it means

Migrations cannot run inside a user-supplied transaction when either (a) any migration command is transaction-suppressed, or (b) the execution strategy retries on failure. Both require EF to control transaction boundaries, which is impossible while the caller holds its own transaction.

Solutions

  1. Do not open a user transaction around migrations; let EF manage the migration transaction.
  2. If you must retry, rely on the retrying execution strategy without an outer user transaction.
  3. Avoid TransactionSuppressed migration commands when running migrations programmatically inside app transactions.
  4. Move Migrate() outside any ambient TransactionScope before calling it.

Example fix

// before
using var tx = context.Database.BeginTransaction();
await context.Database.MigrateAsync(); // throws

// after
await context.Database.MigrateAsync();
// run app logic / transactions after migrations complete
Defensive patterns

Strategy: validation

Validate before calling

bool hasUserTx = context.Database.CurrentTransaction is not null;
bool retries = context.Database.GetExecutionStrategy().RetriesOnFailure;
if (!hasUserTx)
{
    context.Database.Migrate();
}

Type guard

static bool CanMigrateInCurrentState(DbContext context, IEnumerable<MigrationCommand> cmds)
    => context.Database.CurrentTransaction is null
       || (!cmds.Any(c => c.TransactionSuppressed)
           && !context.Database.GetExecutionStrategy().RetriesOnFailure);

Prevention

When it happens

Trigger: Calling Migrate()/MigrateAsync() (or the lower-level IMigrationCommandExecutor.ExecuteNonQuery) while a user transaction is active on the connection (connection.CurrentTransaction set, executionState.Transaction null), and at least one command has TransactionSuppressed or the execution strategy has RetriesOnFailure.

Common situations: Wrapping Database.Migrate() in an outer IDbContextTransaction; using SQL Server's retrying ExecutionStrategy together with migrations; manually opening a transaction before EnsureCreated/Migrate in startup code.

Related errors


AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11). Data as JSON: /api/errors/808a87924c700d30. Report an issue: GitHub.

Appendix: source

Thrown at src/EFCore.Relational/Migrations/Internal/MigrationCommandExecutor.cs:52

    /// <summary>
    ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
    ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
    ///     any release. You should only use it directly in your code with extreme caution and knowing that
    ///     doing so can result in application failures when updating to a new Entity Framework Core release.
    /// </summary>
    public virtual int ExecuteNonQuery(
        IReadOnlyList<MigrationCommand> migrationCommands,
        IRelationalConnection connection,
        MigrationExecutionState executionState,
        bool commitTransaction,
        IsolationLevel? isolationLevel = null)
    {
        var inUserTransaction = connection.CurrentTransaction is not null && executionState.Transaction == null;
        if (inUserTransaction
            && (migrationCommands.Any(x => x.TransactionSuppressed) || executionStrategy.RetriesOnFailure))
        {
            throw new NotSupportedException(RelationalStrings.TransactionSuppressedMigrationInUserTransaction);
        }

        using var transactionScope = new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled);

        return executionStrategy.Execute(
            (migrationCommands, connection, inUserTransaction, executionState, commitTransaction, isolationLevel),
            static (_, s) => Execute(
                s.migrationCommands,
                s.connection,
                s.executionState,
                beginTransaction: !s.inUserTransaction,
                commitTransaction: !s.inUserTransaction && s.commitTransaction,
                s.isolationLevel),
            verifySucceeded: null);
    }

    private static int Execute(
        IReadOnlyList<MigrationCommand> migrationCommands,

View on GitHub (pinned to 3a2006ef56)