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

  1. Move any work that calls SyncAsync/other sync operations out of the HTTP handler callback; queue it and execute after the callback returns.
  2. Use a separate TursoSyncDatabase/TursoConnection instance inside the handler if independent access is required.
  3. 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

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


AI-assisted analysis of tursodatabase/turso@c1e5928725 (2026-08-31). Data as JSON: /api/errors/5d3c4c085b17a13a. Report an issue: GitHub.