tursodatabase/turso · error · InvalidOperationException

Managed transactions cannot be completed through raw transac

Error message

Managed transactions cannot be completed through raw transaction-control SQL.

What it means

MarkCompletedExternally is invoked when the database reports the transaction ended via raw SQL (e.g. COMMIT or ROLLBACK executed as a statement). For managed transactions, whose lifecycle is owned by the driver's replication/sync machinery, completing them through raw SQL is unsupported, so the library throws InvalidOperationException.

Source

Thrown at bindings/dotnet/src/Turso.Data.Sqlite/SqliteTransaction.cs:255

            }
            finally
            {
                Complete();
            }
        }
        else if (disposing && !_completed && _connection is { State: ConnectionState.Open })
            Rollback();
        else if (disposing && _connection is not null && ReferenceEquals(_connection.Transaction, this))
            _connection.Transaction = null;

        base.Dispose(disposing);
    }

    internal void MarkCompletedExternally(bool rolledBack)
    {
        if (_managedTransaction is not null)
        {
            throw new InvalidOperationException(
                "Managed transactions cannot be completed through raw transaction-control SQL.");
        }

        if (rolledBack)
        {
            _externalRollback = true;
            return;
        }

        Complete();
    }

    private void Complete()
    {
        var connection = _connection;
        if (connection is null)
        {
            _completed = true;

View on GitHub (pinned to 6c72522679)

Solutions

  1. Use SqliteTransaction.BeginTransaction/Commit/Rollback instead of raw transaction-control SQL on managed connections.
  2. Remove COMMIT/ROLLBACK statements from ad-hoc SQL scripts executed on managed-transaction connections.
  3. Catch InvalidOperationException and re-run the work using the transaction API if raw SQL cannot be avoided.

Example fix

// before
await conn.ExecuteNonQueryAsync("COMMIT;");
// after
await managedTx.CommitAsync();
Defensive patterns

Strategy: validation

Validate before calling

if (usingManagedTransaction)
    throw new NotSupportedException("Do not run raw BEGIN/COMMIT/ROLLBACK SQL on managed transactions.");

Try / catch

try { await conn.ExecuteNonQueryAsync(sql); }
catch (InvalidOperationException ex) when (ex.Message.Contains("Managed transactions"))
{ /* reroute to managedTx.CommitAsync/RollbackAsync */ }

Prevention

When it happens

Trigger: Executing transaction-control SQL (BEGIN/COMMIT/ROLLBACK) directly through a TursoCommand/ExecuteNonQuery while a managed transaction (created via the managed-transaction API, e.g. on a replica/sync-enabled connection) is active.

Common situations: Porting ADO.NET code that issues 'BEGIN; ...; COMMIT;' strings manually; stored scripts containing transaction control run against a sync-enabled connection with an active managed transaction.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of tursodatabase/turso@6c72522679 (2026-09-06). Data as JSON: /api/errors/8a9ee343d0097638. Report an issue: GitHub.