tursodatabase/turso · error · TursoServerlessException
SQL execution failed
Error message
SQL execution failed
What it means
ExecuteAsync streams a cursor batch and inspects each result entry. An entry of type step_error or error throws TursoServerlessException with the server's message, falling back to 'SQL execution failed' only when the error entry carried no message. It marks a statement that failed on the server at execute time.
Source
Thrown at bindings/dotnet/src/Turso.Serverless.Client/TursoSession.cs:127
rows.Add(new TursoRow(values, rowColumns ?? []));
}
break;
case "step_end":
if (entry.AffectedRowCount is { } affected)
{
rowsAffected = affected;
}
if (entry.LastInsertRowid is { } rowid)
{
lastInsertRowid = rowid;
}
break;
case "step_error":
case "error":
throw new TursoServerlessException(entry.Error?.Message ?? "SQL execution failed", entry.Error?.Code);
}
}
UpdateAutocommitForTransactionControlStatement(sql);
return new TursoResultSet(columns, columnTypes, rows, rowsAffected, lastInsertRowid);
}
internal async Task<TursoBatchResult> BatchAsync(IReadOnlyList<HranaStatement> statements, TursoBatchMode? mode, TimeSpan? queryTimeout, CancellationToken cancellationToken)
{
var userSteps = statements.Select(static s => new HranaBatchStep { Statement = s }).ToList();
List<HranaBatchStep> steps;
var firstUserStepIdx = 0;
var lastUserStepIdx = userSteps.Count - 1;
var beginIdx = -1;
var commitIdx = -1;
var rollbackIdx = -1;
View on GitHub (pinned to 244cde92a7)
Solutions
- Inspect the exception Message and Code (e.g. SQLITE_CONSTRAINT_UNIQUE) — the server message is present in the common case
- Fix the data or statement per the constraint named by the code; add pre-validation for the specific constraint
- For lock/busy codes, retry with backoff or shorten the transaction
- Reproduce with turso db shell against the same database and row
Example fix
// before
await conn.ExecuteAsync("INSERT INTO users(id) VALUES (@id)", cmd); // duplicate id
// after
var exists = await conn.ExecuteAsync("SELECT 1 FROM users WHERE id=@id", cmd);
if (exists.Rows.Count == 0)
await conn.ExecuteAsync("INSERT INTO users(id) VALUES (@id)", cmd); Defensive patterns
Strategy: try-catch
Validate before calling
if (await ExistsAsync(conn, "users", row.Id)) return; // pre-check for the common UNIQUE case
Try / catch
try { await conn.ExecuteAsync(sql, cmd, timeout, ct); } catch (TursoServerlessException ex) when (ex.Code?.StartsWith("SQLITE_CONSTRAINT") == true) { log.Warn($"constraint {ex.Code}: {ex.Message}"); throw; } catch (TursoServerlessException ex) when (ex.Code is "SQLITE_BUSY" or "SQLITE_LOCKED") { await Task.Delay(backoff); await conn.ExecuteAsync(sql, cmd, timeout, ct); } Prevention
- Pre-validate uniqueness/NOT NULL constraints for user-supplied data
- Keep transactions short to limit lock contention
- Always log ex.Code — it disambiguates constraint vs lock vs syntax
When it happens
Trigger: Runtime SQL failures: UNIQUE/FOREIGN KEY/CHECK constraint violations, NOT NULL on insert, type affinity errors, or accessing a locked table — e.g. INSERT duplicating a primary key that validation did not catch.
Common situations: Concurrent writers violating constraints, payload regressions feeding bad data, statements valid against one schema but failing against another, transaction conflicts on busy databases.
Related errors
- Only finite numbers (not Infinity or NaN) can be passed as a
- Unexpected token for last_insert_rowid: {reader.TokenType}
- Describe execution failed
- Unexpected describe response
- Embedded replica sync is not supported yet by the .NET provi
AI-assisted analysis of tursodatabase/turso@244cde92a7 (2026-08-20).
Data as JSON: /api/errors/520f3b901a1e82e6.
Report an issue: GitHub.