Skip to main content

Fixed: "The configured execution strategy 'MySQLRetryingExecutionStrategy' does not support user-initiated transactions" in EF Core

· 5 min read
Jagdish Kumawat
Founder @ Dewiride

As soon as I wrapped two writes in BeginTransactionAsync, the endpoint failed with The configured execution strategy 'MySQLRetryingExecutionStrategy' does not support user-initiated transactions. The message tells you the method to use, and here's the shape of the code it means.

What I Was Doing​

I had two writes in a .NET C# Web API that must happen together: add to a product's stock, and record why. EF Core 10.0.14 and Oracle's provider had EnableRetryOnFailure on. So I added a transaction around them, the way the EF Core docs first show it:

src/Dewiride.Inventory.Api/Features/Stock/StockEndpoints.cs (excerpt)
var now = clock.GetUtcNow().UtcDateTime;

// Nothing below is permanent until CommitAsync. Leaving this method without
// committing rolls everything back, when "await using" disposes of the transaction.
await using var transaction = await db.Database.BeginTransactionAsync(cancellationToken);

What I Expected​

Both writes in one transaction.

What I Got​

Response
HTTP/1.1 500 Internal Server Error
Content-Type: application/problem+json

{
"type": "https://tools.ietf.org/html/rfc9110#section-15.6.1",
"title": "An error occurred while processing your request.",
"status": 500,
"traceId": "00-65717fec709fd3f5ab421c2e31dc08ee-f7aa33615fa7b36c-00"
}
API log
System.InvalidOperationException: The configured execution strategy 'MySQLRetryingExecutionStrategy' does not support user-initiated transactions. Use the execution strategy returned by 'DbContext.Database.CreateExecutionStrategy()' to execute all the operations in the transaction as a retriable unit.

An endpoint answering 500, with the log line System.InvalidOperationException: The configured execution strategy 'MySQLRetryingExecutionStrategy' does not support user-initiated transactions.

What I Checked​

Exactly when it fails. A transaction with no statement in it doesn't fail: BeginTransactionAsync followed by CommitAsync works. The error comes from the first statement inside.

The docs. EF Core's connection resiliency page has this error, with the SQL Server name in it, and says why: "if your code initiates a transaction using BeginTransactionAsync() you are defining your own group of operations that need to be treated as a unit, and everything inside the transaction would need to be played back if a failure occurs."

Why It Happens​

With a retrying strategy, EF Core retries each statement by itself. Inside a transaction, it can't: if the connection drops, the transaction is gone, and repeating one statement would lose the ones before it. So it refuses, and asks you to mark the whole group as one thing to repeat.

The Fix​

Put the whole transaction in a delegate, and give that to the strategy:

src/Dewiride.Inventory.Api/Features/Stock/StockEndpoints.cs (excerpt)
var strategy = db.Database.CreateExecutionStrategy();

return await strategy.ExecuteAsync(
async Task<Results<Created<StockChangeResponse>, NotFound>> (CancellationToken ct) =>
{
var now = clock.GetUtcNow().UtcDateTime;

// Nothing below is permanent until CommitAsync. Leaving this method without
// committing rolls everything back, when "await using" disposes of the transaction.
await using var transaction = await db.Database.BeginTransactionAsync(ct);
// ...the two writes...

await transaction.CommitAsync(ct);

return TypedResults.Created(/* ... */);
},
cancellationToken);

The page's advice: "The solution is to manually invoke the execution strategy with a delegate representing everything that needs to be executed."

Two things about that lambda:

  • It has to state its return type, when it returns more than one kind of result. Without it, you get CS8031.
  • It can run more than once. With a DbContext from dependency injection, start it with db.ChangeTracker.Clear(), or a retry can save a row twice.

After the Fix​

Response
HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8
Location: /api/v1/products/9/stock/movements

{
"movement": {
"id": 2,
"productId": 9,
"type": "Receipt",
"quantityChange": 5,
"reason": "Second delivery",
"reference": "PO-1003",
"orderId": null,
"occurredAtUtc": "2026-11-05T05:12:38.543732Z"
},
"quantityOnHand": 15
}
API log (the transaction)
dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200]
Began transaction with isolation level 'Unspecified'.
dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20210]
Committing transaction.
dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202]
Committed transaction.
dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204]
Disposing transaction.

An endpoint answering 201 Created, with log lines showing Began transaction, then Committing and Committed transaction.

Common Mistakes​

MistakeWhat happensDo this instead
Turning retries off to make the error go awayNo retries anywhereUse ExecuteAsync
Calling BeginTransactionAsync before ExecuteAsyncThe same errorBegin it inside the delegate
Keeping non-database work in the delegateIt runs again on a retry, such as a second emailDo it after the commit
Reusing a scoped DbContext with no ChangeTracker.Clear()A retry can save a row twiceClear it first

Troubleshooting​

SymptomLikely causeFix
Async lambda expression converted to a 'Task' returning delegate cannot return a valueThe lambda needs a return typeThe fix
The message names a different strategy classAnother provider, or your own strategyThe same fix
The same error with TransactionScopeAn ambient transaction counts tooPut it in the delegate
Retries never happenOracle's provider retries nothing by defaultList the error numbers

Conclusion​

A retrying strategy can't replay a transaction it doesn't know about. Call db.Database.CreateExecutionStrategy(), and put BeginTransactionAsync, your work and CommitAsync inside its ExecuteAsync.

Additional Resources​

Stay Updated

Subscribe to our newsletter for the latest tutorials, tech insights, and developer news.

By subscribing, you agree to our privacy policy. Unsubscribe at any time.