tursodatabase/turso · error · InvalidOperationException

The connection is already open.

Error message

The connection is already open.

What it means

SqliteConnection.Open() throws InvalidOperationException when the connection already holds a native database handle (_database is not null). The provider does not treat a second Open as a no-op (unlike some ADO.NET providers); it demands one Open per connection instance. State derives directly from whether the native handle exists, so this fires on any duplicate Open.

Source

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

        ObjectDisposedException.ThrowIf(_disposed, this);
        if (_managedConnection is not null)
        {
            var managedOriginalState = State;
            try
            {
                _managedConnection.Open();
                RegisterManagedReplicaCallbacks();
            }
            finally
            {
                NotifyManagedStateChange(managedOriginalState);
            }

            return;
        }

        if (_database is not null)
            throw new InvalidOperationException("The connection is already open.");
        if (!string.IsNullOrEmpty(_connectionOptions.Password))
            throw new InvalidOperationException(Properties.Resources.EncryptionNotSupported("e_sqlite3"));

        var originalState = State;
        var filename = NormalizeDataSource(_connectionOptions);
        var readOnly = _connectionOptions.Mode == SqliteOpenMode.ReadOnly;
        var sharedMemoryPath = IsSharedMemory(_connectionOptions) ? RegisterSharedMemoryFile(filename) : null;
        try
        {
            _database = TursoBindings.OpenDatabase(filename);
            _dataSource = filename;
            _readOnly = readOnly;
            _sharedMemoryPath = sharedMemoryPath;
            ApplyExtensionSettings();
            ApplyConnectionOptions();
            RegisterScalarFunctions();
            RegisterAggregateFunctions();
            RegisterCollations();

View on GitHub (pinned to 6c72522679)

Solutions

  1. Guard opens: 'if (conn.State != ConnectionState.Open) conn.Open();'.
  2. Restrict opening to one owner of the connection (e.g. the factory or the outermost scope), not every consumer.
  3. For concurrent access, create one SqliteConnection per task/operation instead of sharing and double-opening one instance.

Example fix

// before
var conn = new SqliteConnection(cs);
conn.Open();
DoWork(conn);
DoMoreWork(conn); // internally calls conn.Open() -> throws

// after
var conn = new SqliteConnection(cs);
if (conn.State != ConnectionState.Open) conn.Open();
DoWork(conn);
DoMoreWork(conn); // guarded open is a no-op
Defensive patterns

Strategy: validation

Validate before calling

static void EnsureOpen(SqliteConnection conn)
{
    if (conn.State != ConnectionState.Open)
        conn.Open();
}

Prevention

When it happens

Trigger: Calling conn.Open() twice without an intervening Close/Dispose; 'ensure open' helpers that unconditionally call Open; multiple components sharing one SqliteConnection and each opening it; retry wrappers that re-open after a transient failure without checking state.

Common situations: Shared connections in DI services where several code paths open independently, generic ADO.NET infrastructure ported from SqlClient (where Open is often idempotent in practice via pooling), and race conditions where two tasks open the same instance concurrently.

Related errors


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