tursodatabase/turso · error · InvalidOperationException
A transaction is already active on this connection.
Error message
A transaction is already active on this connection.
What it means
BeginRemoteTransaction tracks one _remoteTransactionActive flag per connection and refuses a second BEGIN with 'A transaction is already active on this connection.' The remote path supports at most one outstanding transaction per connection: no nested transactions and no concurrent ones on a shared connection.
Source
Thrown at bindings/dotnet/src/Turso.Data/TursoConnection.cs:236
.ConfigureAwait(false);
}
catch (TursoRemoteSqlException)
{
throw;
}
catch
{
InvalidateRemoteSession();
throw;
}
}
internal void BeginRemoteTransaction(IsolationLevel isolationLevel)
{
_ = isolationLevel;
var remoteClient = _remoteClient ?? throw new InvalidOperationException("Turso database is closed.");
if (_remoteTransactionActive)
throw new InvalidOperationException("A transaction is already active on this connection.");
_remoteTransactionActive = true;
try
{
remoteClient
.ExecuteAsync("BEGIN", new TursoParameterCollection(), wantRows: false, DefaultTimeout, closeAfter: false, CancellationToken.None)
.GetAwaiter()
.GetResult();
}
catch (TursoRemoteSqlException)
{
_remoteTransactionActive = false;
throw;
}
catch
{
InvalidateRemoteSession();
throw;View on GitHub (pinned to 244cde92a7)
Solutions
- Commit or Rollback the existing TursoTransaction before beginning another on the same connection
- Use one connection per concurrent transaction; never share a TursoConnection across threads for transactional work
- Restructure nested 'transactional' methods to accept an optional existing transaction instead of always opening a new one
Example fix
// before
var tx1 = conn.BeginTransaction();
var tx2 = conn.BeginTransaction(); // throws: already active
// after
using (var tx = conn.BeginTransaction())
{
// all work, including nested helper calls, joins tx
tx.Commit();
} Defensive patterns
Strategy: validation
Validate before calling
// one transaction per connection at a time -- serialize access
await _connectionGate.WaitAsync();
try
{
using var tx = conn.BeginTransaction();
/* work */ tx.Commit();
}
finally { _connectionGate.Release(); } Type guard
static bool SupportsNestedTransactions => false;
Try / catch
try { var tx = conn.BeginTransaction(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("already active"))
{
// commit/rollback the existing transaction first, or use a separate connection
} Prevention
- Commit or Rollback before beginning another transaction on the same connection
- Never share one connection between concurrent transactions
- Pass an optional ambient transaction into nested methods instead of opening a new one
When it happens
Trigger: Calling connection.BeginTransaction() twice without Commit/Rollback in between; two threads or requests sharing one connection each calling BeginTransaction; repository methods that each wrap their work in a transaction and call each other.
Common situations: Recursive service methods with [Transactional]-style wrappers; a pooled connection reused by concurrent requests that both begin transactions; a missing commit in an error path leaving the transaction open.
Related errors
- No remote transaction is active on this connection.
- Sync requires an embedded replica connection.
- Embedded replica sync is not supported yet by the .NET provi
- Turso does not support changing the active database.
- Embedded replica connections are not supported yet by the .N
AI-assisted analysis of tursodatabase/turso@244cde92a7 (2026-08-20).
Data as JSON: /api/errors/a2bd6ce12749974f.
Report an issue: GitHub.