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

  1. Commit or Rollback the existing TursoTransaction before beginning another on the same connection
  2. Use one connection per concurrent transaction; never share a TursoConnection across threads for transactional work
  3. 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

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


AI-assisted analysis of tursodatabase/turso@244cde92a7 (2026-08-20). Data as JSON: /api/errors/a2bd6ce12749974f. Report an issue: GitHub.