{"record":{"id":"26b4a9c37a66f0ac","repo":"tursodatabase/turso","slug":"embedded-replica-path-is-closing","errorCode":null,"errorMessage":"Embedded replica '{path}' is closing.","messagePattern":"Embedded replica '(.+?)' is closing\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"bindings/dotnet/src/Turso.Data/TursoReplicaRegistry.cs","lineNumber":39,"sourceCode":"        if (options.Path == \":memory:\")\n        {\n            var database = await TursoSyncDatabase\n                .CreateAsync(options, cancellationToken)\n                .ConfigureAwait(false);\n            return Lease.CreateStandalone(\n                database,\n                new TursoAutomaticSyncCoordinator(database, syncInterval, timeProvider));\n        }\n\n        var path = Path.GetFullPath(options.Path);\n        var fingerprint = Fingerprint.Create(options, syncInterval);\n        Entry entry;\n        lock (Gate)\n        {\n            if (Entries.TryGetValue(path, out entry!))\n            {\n                if (entry.Closing)\n                    throw new InvalidOperationException($\"Embedded replica '{path}' is closing.\");\n                if (!pooling || !entry.Pooling)\n                    throw new InvalidOperationException($\"Embedded replica '{path}' is already open exclusively.\");\n                if (entry.Fingerprint != fingerprint)\n                    throw new InvalidOperationException($\"Embedded replica '{path}' is already open with different options.\");\n\n                checked\n                {\n                    entry.ReferenceCount++;\n                }\n            }\n            else\n            {\n                entry = new Entry(path, pooling, fingerprint)\n                {\n                    ReferenceCount = 1,\n                };\n                entry.Initialization = InitializeAsync(entry, options, syncInterval, timeProvider);\n                Entries.Add(path, entry);","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/tursodatabase/turso/blob/6c7252267988c76e632af00a671e4b9788dfae13/bindings/dotnet/src/Turso.Data/TursoReplicaRegistry.cs#L21-L57","documentation":"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.","triggerScenarios":"Opening a new connection to an embedded replica at the same database path while a previous connection's close/dispose is still in progress.","commonSituations":"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.","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."],"exampleFix":"// before\nawait oldConn.DisposeAsync();\nvar newConn = new TursoConnection(replicaPath); // may race with close\nawait newConn.OpenAsync();\n// after\nawait oldConn.DisposeAsync();\nawait oldReplica.CloseAsync(); // ensure registry entry fully closed\nvar newConn = await TursoReplicaRegistry.AcquireAsync(path, ...);","handlingStrategy":"retry","validationCode":"// No public pre-check; serialize close/open yourself:\nawait closeLock.WaitAsync();\ntry { await oldReplica.CloseAsync(); var conn = await AcquireAsync(path, ...); }\nfinally { closeLock.Release(); }","typeGuard":null,"tryCatchPattern":"for (int attempt = 0; ; attempt++)\n{\n  try { conn = await registry.AcquireAsync(path, ...); break; }\n  catch (InvalidOperationException ex) when (ex.Message.Contains(\"is closing\") && attempt < 5)\n  { await Task.Delay(100 * (attempt + 1)); }\n}","preventionTips":["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."],"tags":["csharp","dotnet","replica","lifecycle","race"],"backgroundTag":"invalid-state-transition","analyzedSha":"6c7252267988c76e632af00a671e4b9788dfae13","analyzedAt":"2026-09-06T07:15:26.990Z","contentChangedAt":"2026-09-06T07:15:26.990Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}