tursodatabase/turso · error · InvalidOperationException
Command was not prepared.
Error message
Command was not prepared.
What it means
After Prepare() returns on the local path, Execute() expects the compiled TursoStatementHandle in _statement (TursoCommand.cs:276-278). 'Command was not prepared' means that invariant broke: most plausibly a TursoCommand subclass overrode Prepare() as a no-op without calling base, or the connection's remote client appeared between Execute's IsRemote check and Prepare's early remote return, leaving _statement null.
Source
Thrown at bindings/dotnet/src/Turso.Data/TursoCommand.cs:287
: value.Equals("ON", StringComparison.OrdinalIgnoreCase)
|| value.Equals("TRUE", StringComparison.OrdinalIgnoreCase)
|| value.Equals("YES", StringComparison.OrdinalIgnoreCase);
}
private DbDataReader Execute(CommandBehavior behavior = CommandBehavior.Default)
{
if (_connection is null)
throw new InvalidOperationException("Connection must be set before executing a command.");
if (_connection.IsRemote)
return ExecuteRemoteAsync(behavior, CancellationToken.None).GetAwaiter().GetResult();
IDisposable? syncOperation = _connection.EnterSyncOperation();
try
{
PrepareCore();
var statement = _statement ?? throw new InvalidOperationException("Command was not prepared.");
_statement = null;
try
{
var reader = new TursoDataReader(this, statement, behavior, syncOperation);
syncOperation = null;
return reader;
}
catch
{
statement.Dispose();
throw;
}
}
finally
{
syncOperation?.Dispose();
}
}View on GitHub (pinned to c1e5928725)
Solutions
- Do not override Prepare(); if you must, always call base.Prepare() first.
- Use one command per thread and prefer creating commands per execution instead of sharing instances.
- Avoid opening/closing the connection concurrently with execution on the same objects.
Example fix
// before
public class MyCommand : TursoCommand
{
public override void Prepare() { /* no base call */ }
}
// after
public class MyCommand : TursoCommand
{
public override void Prepare() { base.Prepare(); /* extra work */ }
} Defensive patterns
Strategy: try-catch
Validate before calling
if (cmd is not TursoCommand || cmd.Connection is not TursoConnection tc || tc.IsRemote)
throw new InvalidOperationException("Local prepared execution requires a bound local TursoConnection."); Try / catch
try
{
using var reader = cmd.ExecuteReader();
}
catch (InvalidOperationException ex) when (ex.Message == "Command was not prepared.")
{
// Invariant break: re-create the command from the connection and execute once more.
using var fresh = conn.CreateCommand();
fresh.CommandText = cmd.CommandText;
using var reader2 = fresh.ExecuteReader();
} Prevention
- Never override Prepare() without calling base.Prepare().
- Do not share a command across threads.
- Keep Open/Close of the connection off threads that are executing commands.
When it happens
Trigger: Subclassing TursoCommand and overriding Prepare() without base.Prepare(); racing Open/Close of the connection from another thread while a local Execute is in flight.
Common situations: Exotic customization of command behavior; multithreaded sharing of one command/connection pair; not produced by ordinary sequential use of the shipped API.
Related errors
- CommandText must be set before preparing a command.
- Connection must be set before preparing a command.
- Turso batch execution is currently supported only for remote
- TursoBatchCommand only supports CommandType.Text.
- Batch command must be a TursoBatchCommand.
AI-assisted analysis of tursodatabase/turso@c1e5928725 (2026-08-31).
Data as JSON: /api/errors/ad85c2e99311ca69.
Report an issue: GitHub.