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
- Await completion of the prior connection/replica dispose before opening a new one on the same path.
- Retry AcquireAsync after the close finishes (with small backoff).
- Reuse the existing open connection instead of closing and reopening.
- 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
- Await full disposal/close before reopening the same replica path.
- Reuse a single long-lived connection per replica path.
- Serialize open/close of replicas with a lock or semaphore.
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
- {method} requires an open data reader.
- Embedded replica '{path}' is already open exclusively.
- Embedded replica sync is not supported yet by the .NET provi
- Embedded replica connections are not supported yet by the .N
- Sync Interval requires embedded replica support, which is no
AI-assisted analysis of tursodatabase/turso@6c72522679 (2026-09-06).
Data as JSON: /api/errors/26b4a9c37a66f0ac.
Report an issue: GitHub.