tursodatabase/turso · error · SqliteException
SqliteCommand.ToSqliteException(ex, sql)
Error message
SqliteCommand.ToSqliteException(ex, sql)
What it means
When a savepoint-control statement issued by SqliteTransaction (Save, Rollback, Release) fails at the engine level, the underlying Turso.Raw.Public.TursoException is converted via SqliteCommand.ToSqliteException(ex, sql) into a SqliteException carrying the failing SQL text. This wrapper exists so callers using the Microsoft.Data.Sqlite-compatible surface see a SqliteException instead of a raw Turso exception. The original native error code and message are preserved; the SQL statement is attached for diagnosis.
Source
Thrown at bindings/dotnet/src/Turso.Data.Sqlite/SqliteTransaction.cs:341
{
if (_managedTransaction is null)
{
Execute(sql);
return;
}
try
{
using var command = new global::Turso.TursoCommand(_connection!.ManagedConnection)
{
CommandText = sql,
Transaction = _managedTransaction,
};
command.ExecuteNonQuery();
}
catch (Turso.Raw.Public.TursoException ex)
{
throw SqliteCommand.ToSqliteException(ex, sql);
}
}
private async Task ExecuteTransactionCommandAsync(string sql, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (_managedTransaction is null)
{
Execute(sql);
return;
}
try
{
await using var command = new global::Turso.TursoCommand(_connection!.ManagedConnection)
{
CommandText = sql,
Transaction = _managedTransaction,View on GitHub (pinned to 6c72522679)
Solutions
- Inspect SqliteException.SqliteErrorCode and Message to identify the underlying native error (busy, misuse, I/O) and address that root cause.
- Verify the connection is open and the transaction has not already been completed before calling Save/Rollback/Release (ThrowIfCompleted guards the managed state, but the native side can still be stale).
- Ensure savepoint/release calls are properly nested and each name is released only once on a single thread.
- If the error is transient (busy/locked), retry the operation after the competing transaction completes or increase the busy timeout on the connection.
Example fix
// before: releasing a savepoint without checking state
transaction.Save();
doWork();
transaction.Release();
// after: guard state and handle transient failures
if (transaction.Connection == null) throw new InvalidOperationException("transaction completed");
transaction.Save();
doWork();
try { transaction.Release(); }
catch (SqliteException ex) when (ex.SqliteErrorCode == 5 /* SQLITE_BUSY */)
{
// retry after the competing writer finishes
} Defensive patterns
Strategy: try-catch
Validate before calling
if (transaction == null || transaction.Connection == null || transaction.Connection.State != ConnectionState.Open)
throw new InvalidOperationException("Cannot use a completed transaction's connection."); Try / catch
try
{
transaction.Release();
}
catch (SqliteException ex)
{
// ex.SqliteErrorCode and ex.Message carry the wrapped native error and the failing SQL
if (ex.SqliteErrorCode == 5) { /* SQLITE_BUSY: retry */ }
else throw;
} Prevention
- Always check transaction.Connection is not null before Save/Rollback/Release.
- Keep savepoint usage strictly nested and single-threaded per connection.
- Wrap transaction body in try/catch that rolls back on failure so later Release calls never hit an already-aborted transaction.
- Set an adequate busy timeout when other writers may hold the database.
When it happens
Trigger: Calling SqliteTransaction.Save(), SqliteTransaction.Rollback(), or SqliteTransaction.Release() on a transaction backed by a managed Turso transaction (_managedTransaction != null) where ExecuteNonQuery of the SAVEPOINT / ROLLBACK TO ... RELEASE statement raises TursoException - e.g. the connection dropped, the savepoint name is no longer valid, the transaction was already rolled back at the native layer, or an I/O error occurred while stepping the statement.
Common situations: Saving or releasing a savepoint after the underlying connection was closed or reset by the network/server; nesting savepoints incorrectly so a RELEASE references a name that was already released; concurrent access to the same connection from another thread causing the native transaction to be invalidated; database file locked or disk I/O failure during the statement step.
Understand the failure class
Background: "query failed", "%w: SQL error" — wrapped database query errors in Go libraries explained — this error's family across 3 libraries.
Related errors
- Transaction must be a TursoTransaction.
- Transaction must be a SqliteTransaction.
- The transaction connection does not match the command connec
- The transaction has completed.
- Execute requires the command to have a transaction when the
AI-assisted analysis of tursodatabase/turso@6c72522679 (2026-09-06).
Data as JSON: /api/errors/903be635224ec264.
Report an issue: GitHub.