tursodatabase/turso · error · InvalidOperationException

The transaction has completed.

Error message

The transaction has completed.

What it means

EnsureExecutable rejects execution when the command's transaction is finished: Transaction is { IsCompleted: true } or { WasRolledBackExternally: true } throws InvalidOperationException. Completion is set by Commit()/Rollback(), and external rollback happens when the connection reclaims an abandoned transaction; after that the native transaction context no longer exists and a statement would run in a misleading autocommit mode.

Source

Thrown at bindings/dotnet/src/Turso.Data.Sqlite/SqliteCommand.cs:597

            throw new InvalidOperationException(Properties.Resources.SetRequiresNoOpenReader(property));
    }

    private void EnsureExecutable(string method)
    {
        if (_hasOpenReader)
            throw new InvalidOperationException(Properties.Resources.DataReaderOpen);
        if (Connection is null || Connection.State != ConnectionState.Open)
            throw new InvalidOperationException(Properties.Resources.CallRequiresOpenConnection(method));
        if (Transaction is { IsCompleted: true } or { WasRolledBackExternally: true })
            throw new InvalidOperationException(Properties.Resources.TransactionCompleted);
        if (Transaction is not null && !ReferenceEquals(Transaction.Connection, Connection))
            throw new InvalidOperationException(Properties.Resources.TransactionConnectionMismatch);

        var connectionTransaction = Connection.Transaction;
        if (connectionTransaction is null || ReferenceEquals(Transaction, connectionTransaction))
            return;
        if (connectionTransaction.IsCompleted)
            throw new InvalidOperationException(Properties.Resources.TransactionCompleted);
        if (!IsTransactionControlCommand(CommandText))
            throw new InvalidOperationException(Properties.Resources.TransactionRequired);
    }

    private void CloseReader()
    {
        _hasOpenReader = false;
        Connection?.ReaderClosed();
    }

    internal Action OwnBufferedReader()
    {
        _hasOpenReader = true;
        Connection?.ReaderOpened();
        return () =>
        {
            CloseReader();
            Dispose();

View on GitHub (pinned to 6c72522679)

Solutions

  1. Begin a new transaction for further work after Commit()/Rollback().
  2. Scope command lifetimes inside the transaction's using block.
  3. After a connection-level failure, reopen and re-begin rather than reusing the old pair.

Example fix

// before
tx.Commit();
cmd.ExecuteNonQuery(); // cmd still bound to the completed transaction -> throws

// after
tx.Commit();
using var tx2 = conn.BeginTransaction();
cmd.Transaction = tx2;
cmd.ExecuteNonQuery();
Defensive patterns

Strategy: validation

Validate before calling

// completion is observable via the thrown error; track it locally instead
sealed class TxScope(SqliteConnection conn) {
    SqliteTransaction? tx;
    public bool Active => tx is not null;
    public SqliteTransaction Begin() => tx = conn.BeginTransaction();
    public void Finish() { if (tx is not null) { tx.Dispose(); tx = null; } }
}

if (scope.Active) cmd.ExecuteNonQuery();

Try / catch

catch (InvalidOperationException) when (transactionWasFinished) {
    using var tx2 = conn.BeginTransaction();
    cmd.Transaction = tx2;
    cmd.ExecuteNonQuery();
}

Prevention

When it happens

Trigger: Calling Commit() then reusing the same command/transaction for another statement; executing after Rollback() on an error path that falls through to more SQL; a transaction whose connection was closed or reset (externally rolled back); long-lived commands outliving their transaction.

Common situations: Shared repository commands surviving past a request-scoped transaction; error handlers that roll back and then continue processing; connection close/abort during async flows invalidating the pending transaction.

Related errors


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