tursodatabase/turso · error · InvalidOperationException
Turso sync operations cannot be reentered from the sync HTTP
Error message
Turso sync operations cannot be reentered from the sync HTTP handler.
What it means
TursoSyncDatabase serializes operations on a connection and forbids reentrancy: code running inside the sync HTTP I/O callback (the driver's own HTTP handler serving the embedded replica's local HTTP endpoint) must not start new sync/connection operations. ThrowIfIoReentrant() throws InvalidOperationException when the I/O callback depth is non-zero and a new operation is attempted, preventing deadlocks and state corruption.
Source
Thrown at bindings/dotnet/src/Turso.Data/TursoSyncDatabase.cs:274
{
using var item = TursoSyncBindings.TakeIoItem(_handle);
if (item is not null)
RunSynchronously(() => HandleIoItemAsync(item, CancellationToken.None));
TursoSyncBindings.StepIoCallbacks(_handle);
}
internal IDisposable EnterConnectionOperation()
{
ThrowIfIoReentrant();
ObjectDisposedException.ThrowIf(Volatile.Read(ref _resourcesDisposed) != 0, this);
_operationLock.Wait();
return new ConnectionOperationLease(_operationLock);
}
internal void ThrowIfIoReentrant()
{
if (_ioCallbackDepth.Value != 0)
throw new InvalidOperationException("Turso sync operations cannot be reentered from the sync HTTP handler.");
}
internal async Task<TursoDatabaseHandle> ConnectHandleAsync(CancellationToken cancellationToken)
{
ThrowIfDisposed();
await _operationLock.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
ThrowIfDisposed();
using var operation = StartOperation(
TursoSyncBindings.StartConnect,
TursoSyncOperationKind.Connect);
await DriveOperationAsync(
operation,
TursoSyncOperationKind.Connect,
cancellationToken)
.ConfigureAwait(false);
EnsureResultKind(operation, TursoSyncOperationResultKind.Connection);View on GitHub (pinned to c1e5928725)
Solutions
- Move any work that calls SyncAsync/other sync operations out of the HTTP handler callback; queue it and execute after the callback returns.
- Use a separate TursoSyncDatabase/TursoConnection instance inside the handler if independent access is required.
- Guard shared call sites with a check/flag so they skip reentrant calls during an active sync.
Example fix
// before (inside handler invoked during sync) await syncDatabase.SyncAsync(); // after _syncPending = true; // handle after callback completes
Defensive patterns
Strategy: try-catch
Try / catch
try
{
await connection.SyncAsync(ct);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("reentered"))
{
_logger.LogWarning("Sync reentered from HTTP handler; deferring.");
_deferredSync = true;
} Prevention
- Never call sync/connection APIs from code executing inside the sync HTTP handler.
- Queue handler-triggered work and run it after the callback completes.
- Use separate TursoSyncDatabase instances for handler-side needs.
When it happens
Trigger: Calling SyncAsync, Dispose, EnterConnectionOperation, or any connection operation from within code that itself runs while the sync HTTP handler is servicing a request (ioCallbackDepth > 0) — e.g. a custom handler or app code invoked during HTTP handling that touches the same TursoSyncDatabase.
Common situations: Application code hooked into the sync HTTP pipeline (logging, auth callbacks) that reuses the connection; nested sync calls triggered indirectly while a sync is in flight.
Related errors
- Automatic sync is not supported for embedded replica connect
- The sync remote URL must not include a query string or fragm
- Use AuthToken instead of embedding credentials in the sync U
- The sync remote URL must include a host.
- The sync remote URL must use turso, libsql, HTTP, or HTTPS.
AI-assisted analysis of tursodatabase/turso@c1e5928725 (2026-08-31).
Data as JSON: /api/errors/5d3c4c085b17a13a.
Report an issue: GitHub.