tursodatabase/turso · error
write-write conflict (transaction rolled back)
Error message
write-write conflict (transaction rolled back)
What it means
Reported by the MVCC concurrent-transaction REPL (tursodb --mvcc) when a statement fails with turso_core::LimboError::WriteWriteConflict. Experimental MVCC mode uses optimistic snapshot isolation: each transaction validates its writes against concurrently committed changes. When another connection already committed writes to the same rows/pages since this transaction's snapshot was taken, the engine rolls the losing transaction back and the REPL prints 'ERROR: write-write conflict (transaction rolled back)'.
Source
Thrown at cli/mvcc_repl.rs:142
}
fn execute_and_display(conn: &Arc<Connection>, sql: &str, conn_name: &str) -> anyhow::Result<()> {
let mut stmt = conn.prepare(sql).map_err(|e| anyhow::anyhow!("{}", e))?;
match stmt.run_collect_rows() {
Ok(rows) => {
if rows.is_empty() {
println!("[{conn_name}] OK");
} else {
for row in rows {
let formatted: Vec<String> = row.iter().map(fmt_value).collect();
println!("[{conn_name}] {}", formatted.join(" | "));
}
}
Ok(())
}
Err(LimboError::WriteWriteConflict) => Err(anyhow::anyhow!(
"write-write conflict (transaction rolled back)"
)),
Err(e) => Err(anyhow::anyhow!("{e}")),
}
}
fn fmt_value(v: &Value) -> String {
use turso_core::Numeric;
match v {
Value::Null => "NULL".to_string(),
Value::Numeric(Numeric::Integer(i)) => i.to_string(),
Value::Numeric(Numeric::Float(f)) => {
let fval: f64 = (*f).into();
// Format floats without trailing zeros for cleaner display
if fval.fract() == 0.0 && fval.abs() < 1e10 {
format!("{fval:.1}")
} else {
format!("{fval}")
}View on GitHub (pinned to 0e69fa4af1)
Solutions
- Accept the rollback and retry: the losing transaction is already rolled back, so re-run BEGIN CONCURRENT plus the write statements on the failed connection
- Keep transactions short so fewer concurrent snapshots overlap on the same rows
- Route hot-row writes through a single connection or an application-level lock so commits never interleave
- Re-read data after the other connection commits so the retried transaction starts from a fresh snapshot
Example fix
// before (one-shot, fails on conflict): mvcc> conn2 INSERT INTO t VALUES (42) [conn2] ERROR: write-write conflict (transaction rolled back) // after (rollback + retry): mvcc> conn2 ROLLBACK mvcc> conn2 BEGIN CONCURRENT mvcc> conn2 INSERT INTO t VALUES (42)
Defensive patterns
Strategy: retry
Type guard
fn is_write_write_conflict(err: &turso_core::LimboError) -> bool {
matches!(err, turso_core::LimboError::WriteWriteConflict)
} Try / catch
loop {
conn.execute("BEGIN CONCURRENT")?;
match run_writes(conn) {
Ok(_) => { conn.execute("COMMIT")?; break; }
Err(e) if is_write_write_conflict(&e) => {
let _ = conn.execute("ROLLBACK"); // already rolled back; keep for hygiene
continue; // optionally with backoff
}
Err(e) => return Err(e),
}
} Prevention
- Keep transactions short so snapshots rarely go stale between read and write
- Serialize writes to hot rows through one connection or an app-level lock
- Treat retry-on-conflict as mandatory application logic under optimistic MVCC, not an edge case
- Exercise conflict paths with testing/concurrent-simulator before shipping
When it happens
Trigger: In the REPL, run 'conn1 BEGIN CONCURRENT' and a write (INSERT/UPDATE/DELETE) on conn1, then on conn2 (also in a transaction) write to the same table/rows and let either side commit first; the other connection's next write statement or commit returns WriteWriteConflict via stmt.run_collect_rows().
Common situations: Interactive multi-connection conflict testing (the module doc shows exactly this conn1/conn2 INSERT scenario); scripts that assume lock-based blocking writes instead of optimistic validation; long-running read-then-write transactions whose snapshots go stale while another session commits.
Related errors
- Turso sync operations cannot be reentered from the sync HTTP
- No active remote transaction
- Cannot commit in autocommit mode.
- Cannot rollback in autocommit mode.
- Cannot change isolation level while transaction is active.
AI-assisted analysis of tursodatabase/turso@0e69fa4af1 (2026-08-20).
Data as JSON: /api/errors/663d5f76a11532ab.
Report an issue: GitHub.