tursodatabase/turso · error · InvalidOperationException

{method} requires an open connection.

Error message

{method} requires an open connection.

What it means

BeginTransaction checks State before doing anything and throws InvalidOperationException when the connection is not Open. Unlike some providers that open implicitly, this one requires an explicit Open() first; transactions cannot start against a closed connection because they need a live native database handle. The same CallRequiresOpenConnection guard covers other APIs (BackupDatabase, and the internal DatabaseHandle/EnsureOpen used by command execution).

Source

Thrown at bindings/dotnet/src/Turso.Data.Sqlite/SqliteConnection.cs:344

    public static void ClearPool(SqliteConnection connection)
    {
        ArgumentNullException.ThrowIfNull(connection);
    }

    public new virtual SqliteTransaction BeginTransaction()
        => BeginTransaction(IsolationLevel.Unspecified);

    public virtual SqliteTransaction BeginTransaction(bool deferred)
        => BeginTransaction(IsolationLevel.Unspecified, deferred);

    public new virtual SqliteTransaction BeginTransaction(IsolationLevel isolationLevel)
        => BeginTransaction(isolationLevel, deferred: isolationLevel == IsolationLevel.ReadUncommitted);

    public virtual SqliteTransaction BeginTransaction(IsolationLevel isolationLevel, bool deferred)
    {
        if (State != ConnectionState.Open)
            throw new InvalidOperationException(Properties.Resources.CallRequiresOpenConnection(nameof(BeginTransaction)));
        if (Transaction is not null)
            throw new InvalidOperationException(Properties.Resources.ParallelTransactionsNotSupported);

        Transaction = new SqliteTransaction(this, isolationLevel, deferred);
        return Transaction;
    }

    public virtual void CreateCollation(string name, Comparison<string>? comparison)
    {
        RegisterCollation(name, comparison is null ? null : (left, right) => comparison(left, right));
    }

    public virtual void CreateCollation<T>(string name, T state, Func<T, string, string, int>? comparison)
    {
        RegisterCollation(name, comparison is null ? null : (left, right) => comparison(state, left, right));
    }

    public virtual void CreateFunction<TResult>(string name, Func<TResult>? function, bool isDeterministic = false)

View on GitHub (pinned to 6c72522679)

Solutions

  1. Call conn.Open() before conn.BeginTransaction().
  2. Centralize the check in an EnsureOpen helper: 'if (conn.State != ConnectionState.Open) conn.Open();'.
  3. Verify in debuggers/logs that the same connection instance you transact on is the one that was opened (not a copy or a different field).

Example fix

// before
using var conn = new SqliteConnection(cs);
using var tx = conn.BeginTransaction(); // throws: requires an open connection

// after
using var conn = new SqliteConnection(cs);
conn.Open();
using var tx = conn.BeginTransaction();
Defensive patterns

Strategy: validation

Validate before calling

static SqliteTransaction BeginTransactionSafe(SqliteConnection conn)
{
    if (conn.State != ConnectionState.Open)
        conn.Open();
    return conn.BeginTransaction();
}

Prevention

When it happens

Trigger: 'var conn = new SqliteConnection(cs); var tx = conn.BeginTransaction();' with no Open() in between; BeginTransaction in a unit test whose fixture opens the connection asynchronously and races; helpers that assume lazy connection opening; calling BeginTransaction after Close() on a reused connection object.

Common situations: Ported from providers with implicit open (EF Core patterns usually open for you, raw ADO.NET does not), connection lifecycle split across methods where Open happens later than expected, and mock-heavy tests that never exercise the real open path.

Related errors


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