tursodatabase/turso · error · InvalidOperationException

The transaction connection does not match the command connec

Error message

The transaction connection does not match the command connection.

What it means

EnsureExecutable runs before every Execute and requires that, when the command has a transaction, it was begun on the exact same SqliteConnection instance bound to the command (ReferenceEquals check). Mismatched pairs throw InvalidOperationException, because transaction state is scoped per connection object - a foreign pairing could not be honored by the engine.

Source

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

        }
    }

    private void ThrowIfReaderOpen(string property)
    {
        if (_hasOpenReader)
            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()
    {

View on GitHub (pinned to 6c72522679)

Solutions

  1. Set cmd.Transaction to the transaction begun on the very connection assigned to cmd.Connection.
  2. Create commands via conn.CreateCommand() after BeginTransaction and assign both together.
  3. After any reconnect, re-begin the transaction and re-assign it to the commands.

Example fix

// before
cmd.Connection = connA;
cmd.Transaction = connB.BeginTransaction(); // wrong connection -> mismatch

// after
using var tx = connA.BeginTransaction();
cmd.Connection = connA;
cmd.Transaction = tx;
Defensive patterns

Strategy: validation

Validate before calling

static void Bind(SqliteCommand cmd, SqliteConnection conn, SqliteTransaction tx) {
    cmd.Connection = conn;
    cmd.Transaction = ReferenceEquals(tx.Connection, conn)
        ? tx
        : throw new InvalidOperationException("transaction belongs to another connection");
}

Try / catch

catch (InvalidOperationException) when (cmd.Transaction is not null &&
        !ReferenceEquals(cmd.Transaction.Connection, cmd.Connection)) {
    // re-pair: move the command to the transaction's own connection, then retry
}

Prevention

When it happens

Trigger: cmd.Connection set to connA while cmd.Transaction is a transaction begun on connB (two connections to the same file); pooled/reused command objects carrying an old Transaction after Connection is reassigned; reconnect logic replacing the connection but not the transaction.

Common situations: Request-scoped DI handing different connection instances to data and transaction paths; reconnect-after-failure code paths; parallel workers each holding a connection while sharing command templates.

Related errors


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