tursodatabase/turso · error · InvalidOperationException
No remote transaction is active on this connection.
Error message
No remote transaction is active on this connection.
What it means
CommitRemoteTransaction refuses to send COMMIT when _remoteTransactionActive is false: 'No remote transaction is active on this connection.' The flag is cleared by successful Commit/Rollback, by Close, and by session invalidation, so this error means the connection-level transaction already ended (or never began) while commit was still requested.
Source
Thrown at bindings/dotnet/src/Turso.Data/TursoConnection.cs:262
.GetResult();
}
catch (TursoRemoteSqlException)
{
_remoteTransactionActive = false;
throw;
}
catch
{
InvalidateRemoteSession();
throw;
}
}
internal void CommitRemoteTransaction()
{
var remoteClient = _remoteClient ?? throw new InvalidOperationException("Turso database is closed.");
if (!_remoteTransactionActive)
throw new InvalidOperationException("No remote transaction is active on this connection.");
try
{
remoteClient
.ExecuteAsync("COMMIT", new TursoParameterCollection(), wantRows: false, DefaultTimeout, closeAfter: false, CancellationToken.None)
.GetAwaiter()
.GetResult();
}
catch (TursoRemoteSqlException)
{
throw;
}
catch
{
InvalidateRemoteSession();
throw;
}
View on GitHub (pinned to 244cde92a7)
Solutions
- Commit exactly once per transaction; after Commit or Rollback (or an error path that invalidated the session) treat the transaction as finished
- In finally blocks, commit only if your own flag says the work succeeded and the transaction has not completed
- Do not interleave raw BEGIN/COMMIT SQL with TursoTransaction on the same remote connection
- Catch InvalidOperationException at the boundary and re-verify database state before assuming the commit landed
Example fix
// before
var committed = false;
try { /* work */ committed = true; }
finally { tx.Commit(); } // throws if tx already ended or never began
// after
var committed = false;
try { /* work */ committed = true; }
finally { if (committed && !tx.IsCompleted) tx.Commit(); } Defensive patterns
Strategy: validation
Validate before calling
var ok = false;
try { /* work */ tx.Commit(); ok = true; }
finally { if (!ok && !tx.IsCompleted) tx.Rollback(); } Type guard
static bool IsUsableTransaction(TursoTransaction tx) => !tx.IsCompleted;
Try / catch
try { tx.Commit(); }
catch (InvalidOperationException ex) when (ex.Message == "No remote transaction is active on this connection.")
{
// transaction already ended; verify database state if the outcome matters
} Prevention
- Commit exactly once; treat the transaction as spent afterward
- Guard cleanup commits with a success flag plus completion state
- Do not mix manual BEGIN/COMMIT SQL with TursoTransaction on remote connections
When it happens
Trigger: Calling commit paths twice at the connection level; committing after the transaction was already rolled back or invalidated by an error; transaction bookkeeping desync after mixing manual BEGIN/COMMIT SQL statements with TursoTransaction on a remote connection; committing after Dispose-on-closed-connection completed the transaction out-of-band.
Common situations: Retry loops that re-commit the same unit of work; finally blocks that commit 'for safety' after an explicit rollback; wrappers that hold a TursoTransaction past its real end and commit again in a callback.
Related errors
- A transaction is already active on this connection.
- Encryption Cipher and Encryption Key are local database opti
- Sync requires an embedded replica connection.
- Embedded replica sync is not supported yet by the .NET provi
- Turso does not support changing the active database.
AI-assisted analysis of tursodatabase/turso@244cde92a7 (2026-08-20).
Data as JSON: /api/errors/eeec901e151d3db2.
Report an issue: GitHub.