tursodatabase/turso · error · InvalidOperationException

The connection is not open.

Error message

The connection is not open.

What it means

The internal DatabaseHandle property throws InvalidOperationException('The connection is not open.') whenever _database is null, yet some internal API reached for the native handle. It is the provider's central 'handle exists' gate, used by command preparation, options application, and other internals. Seeing it as a user means you invoked a member (directly or via a SqliteCommand/reader operation) that needs the native database after the connection was closed or before it was opened.

Source

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

        _disposed = true;
        await base.DisposeAsync().ConfigureAwait(false);
        failure?.Throw();
    }

    internal TursoDatabaseHandle DatabaseHandle
    {
        get
        {
            if (_managedConnection is not null)
            {
                if (IsReplica)
                    return ManagedConnection.Turso;
                throw new NotSupportedException(
                    "SQLite native handles are not available for direct remote connections.");
            }

            return _database ?? throw new InvalidOperationException("The connection is not open.");
        }
    }

    internal bool HasOpenReader => _openReaderCount > 0;

    internal bool IsReadOnly => _readOnly;

    internal bool RecursiveTriggers => _recursiveTriggers;

    internal bool ManagedReadYourWrites => _connectionOptions.ReadYourWrites;

    internal void ReaderOpened() => _openReaderCount++;

    internal void ReaderClosed()
    {
        if (_openReaderCount > 0)
            _openReaderCount--;
    }

View on GitHub (pinned to 6c72522679)

Solutions

  1. Re-open the connection (or use a fresh one) before executing further commands.
  2. Scope every command/reader strictly inside the connection's open lifetime ('using var conn' outermost).
  3. For retries, rebuild the command from a live connection instead of replaying a command bound to a dead one.

Example fix

// before
SqliteCommand cmd;
using (var conn = new SqliteConnection(cs))
{
    conn.Open();
    cmd = conn.CreateCommand();
    cmd.CommandText = "SELECT 1";
} // closed
var r = cmd.ExecuteReader(); // DatabaseHandle throws: connection is not open

// after
using (var conn = new SqliteConnection(cs))
{
    conn.Open();
    using var cmd = conn.CreateCommand();
    cmd.CommandText = "SELECT 1";
    using var r = cmd.ExecuteReader();
    while (r.Read()) { /* ... */ }
}
Defensive patterns

Strategy: validation

Validate before calling

static SqliteCommand NewCommandOnLiveConnection(SqliteConnection conn, string sql)
{
    if (conn.State != ConnectionState.Open)
        throw new InvalidOperationException($"Connection is {conn.State}; open it before executing commands.");
    return conn.CreateCommand();
}

Try / catch

try
{
    using var cmd = conn.CreateCommand();
    cmd.CommandText = "SELECT 1";
    cmd.ExecuteScalar();
}
catch (InvalidOperationException ex) when (ex.Message.Contains("not open"))
{
    // handle closed-connection use: reopen or rebuild on a fresh connection
    throw;
}

Prevention

When it happens

Trigger: Executing a command after conn.Close()/Dispose() (e.g. a cached SqliteCommand reused across a close/open cycle); calling EnableExtensions/LoadExtension internals or schema routines on a never-opened connection; async continuations that resume after the connection was disposed; objects that captured the connection when it was open and outlive it.

Common situations: Pooled/cached commands or readers outliving their connection, fire-and-forget tasks racing disposal, retry logic that re-executes against a closed connection, and ordering bugs where Close runs before the last reader.

Related errors


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