tursodatabase/turso · error · InvalidOperationException

Embedded replica '{path}' is closing.

Error message

Embedded replica '{path}' is closing.

What it means

TursoReplicaRegistry.AcquireAsync registers reference-counted access to embedded replicas. If an entry exists for the path but is currently Closing, acquisition throws InvalidOperationException because the replica cannot be safely opened while it is shutting down.

Source

Thrown at bindings/dotnet/src/Turso.Data/TursoReplicaRegistry.cs:39

        if (options.Path == ":memory:")
        {
            var database = await TursoSyncDatabase
                .CreateAsync(options, cancellationToken)
                .ConfigureAwait(false);
            return Lease.CreateStandalone(
                database,
                new TursoAutomaticSyncCoordinator(database, syncInterval, timeProvider));
        }

        var path = Path.GetFullPath(options.Path);
        var fingerprint = Fingerprint.Create(options, syncInterval);
        Entry entry;
        lock (Gate)
        {
            if (Entries.TryGetValue(path, out entry!))
            {
                if (entry.Closing)
                    throw new InvalidOperationException($"Embedded replica '{path}' is closing.");
                if (!pooling || !entry.Pooling)
                    throw new InvalidOperationException($"Embedded replica '{path}' is already open exclusively.");
                if (entry.Fingerprint != fingerprint)
                    throw new InvalidOperationException($"Embedded replica '{path}' is already open with different options.");

                checked
                {
                    entry.ReferenceCount++;
                }
            }
            else
            {
                entry = new Entry(path, pooling, fingerprint)
                {
                    ReferenceCount = 1,
                };
                entry.Initialization = InitializeAsync(entry, options, syncInterval, timeProvider);
                Entries.Add(path, entry);

View on GitHub (pinned to 6c72522679)

Solutions

  1. Await completion of the prior connection/replica dispose before opening a new one on the same path.
  2. Retry AcquireAsync after the close finishes (with small backoff).
  3. Reuse the existing open connection instead of closing and reopening.
  4. Catch InvalidOperationException and treat it as transient during shutdown.

Example fix

// before
await oldConn.DisposeAsync();
var newConn = new TursoConnection(replicaPath); // may race with close
await newConn.OpenAsync();
// after
await oldConn.DisposeAsync();
await oldReplica.CloseAsync(); // ensure registry entry fully closed
var newConn = await TursoReplicaRegistry.AcquireAsync(path, ...);
Defensive patterns

Strategy: retry

Validate before calling

// No public pre-check; serialize close/open yourself:
await closeLock.WaitAsync();
try { await oldReplica.CloseAsync(); var conn = await AcquireAsync(path, ...); }
finally { closeLock.Release(); }

Try / catch

for (int attempt = 0; ; attempt++)
{
  try { conn = await registry.AcquireAsync(path, ...); break; }
  catch (InvalidOperationException ex) when (ex.Message.Contains("is closing") && attempt < 5)
  { await Task.Delay(100 * (attempt + 1)); }
}

Prevention

When it happens

Trigger: Opening a new connection to an embedded replica at the same database path while a previous connection's close/dispose is still in progress.

Common situations: Connection-pool churn: disposing and immediately reopening connections to the same replica path in a race; app restart logic that disposes and recreates connections back-to-back without awaiting shutdown.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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