tursodatabase/turso · error · InvalidOperationException
Transaction-control SQL is not supported in a SqliteBatch. U
Error message
Transaction-control SQL is not supported in a SqliteBatch. Use SqliteConnection.BeginTransaction and SqliteTransaction instead.
What it means
SqliteBatch does not allow BEGIN/COMMIT/ROLLBACK (transaction-control statements) inside batch command text; transactions must be managed through SqliteConnection.BeginTransaction and SqliteTransaction so the batch executor can coordinate them. BuildBatch rejects any parsed statement flagged IsTransactionControl.
Source
Thrown at bindings/dotnet/src/Turso.Data.Sqlite/SqliteBatch.cs:306
};
var statements = new List<ManagedSqliteStatement>();
var managedCommands = new List<global::Turso.TursoBatchCommand>();
var mappings = new List<BatchCommandMapping>(_batchCommands.Count);
try
{
foreach (var batchCommand in _batchCommands.Items)
{
using var command = new SqliteCommand(batchCommand.CommandText, connection, _transaction);
foreach (SqliteParameter parameter in batchCommand.Parameters)
command.Parameters.Add(parameter);
command.ValidateManagedParameterValues();
var commandStatements = ManagedSqliteStatementParser.Parse(batchCommand.CommandText);
if (commandStatements.Count == 0)
throw new InvalidOperationException("Batch command text must contain a SQL statement.");
if (commandStatements.Any(static statement => statement.IsTransactionControl))
{
throw new InvalidOperationException(
"Transaction-control SQL is not supported in a SqliteBatch. "
+ "Use SqliteConnection.BeginTransaction and SqliteTransaction instead.");
}
if (connection.IsReadOnly
&& commandStatements.Any(SqliteCommand.IsWriteStatement))
{
throw new SqliteException(
Properties.Resources.SqliteNativeError(8, "attempt to write a readonly database"),
8);
}
var firstManagedIndex = managedCommands.Count;
foreach (var statement in commandStatements)
{
string sql;
if (preparing)
{
sql = SqliteCommand.RewriteFacadeStatement(statement.Sql, connection);View on GitHub (pinned to 6c72522679)
Solutions
- Remove BEGIN/COMMIT/ROLLBACK statements from the batch text and wrap execution in connection.BeginTransaction()/tx.Commit() instead
- Assign batch.Transaction = (SqliteTransaction)tx so all statements run in that transaction
- Keep transaction control in application code, not in batch SQL
Example fix
// before
batch.Commands.Add(new SqliteBatchCommand { CommandText = "BEGIN; INSERT...; COMMIT;" });
// after
using var tx = connection.BeginTransaction();
batch.Transaction = tx;
batch.Commands.Add(new SqliteBatchCommand { CommandText = "INSERT..." });
await batch.ExecuteReaderAsync();
tx.Commit(); Defensive patterns
Strategy: validation
Validate before calling
var disallowed = new[]{"BEGIN","COMMIT","ROLLBACK","END","SAVEPOINT","RELEASE"};
bool hasTxControl = sql.Split(';').Any(s => disallowed.Contains(s.Trim().Split(' ')[0], StringComparer.OrdinalIgnoreCase)); Type guard
static bool IsTransactionControlSql(string sql) =>
sql.Split(';').Any(s => new[]{"BEGIN","COMMIT","ROLLBACK","END","SAVEPOINT","RELEASE"}
.Contains(s.Trim().Split(' ').FirstOrDefault() ?? "", StringComparer.OrdinalIgnoreCase)); Try / catch
try { await batch.ExecuteReaderAsync(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("Transaction-control")) { /* rewrite SQL without BEGIN/COMMIT, use SqliteTransaction */ } Prevention
- Strip BEGIN/COMMIT/ROLLBACK from scripts destined for SqliteBatch
- Use connection.BeginTransaction()/SqliteTransaction for all transaction scope
- Keep two script variants: interactive (with txn control) and batch (without)
When it happens
Trigger: Including 'BEGIN', 'BEGIN TRANSACTION', 'COMMIT', 'ROLLBACK', 'END', 'SAVEPOINT'/'RELEASE' handling flagged as transaction control in any SqliteBatchCommand.CommandText, then executing the batch.
Common situations: Porting a Microsoft.Data.Sqlite batch script that wrapped work in BEGIN/COMMIT; sharing SQL scripts between psql-style tooling and the batch API; code that appends COMMIT after DML.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Transaction-control SQL is not supported on a managed Sqlite
- Turso batch execution is currently supported only for remote
- TursoBatchCommand only supports CommandType.Text.
- Batch command must be a TursoBatchCommand.
- Transaction must be a TursoTransaction.
AI-assisted analysis of tursodatabase/turso@6c72522679 (2026-09-06).
Data as JSON: /api/errors/6c0139d7c6a74079.
Report an issue: GitHub.