clockworklabs/SpacetimeDB · critical
transaction retry failed again
Error message
transaction retry failed again
What it means
After the anonymous transaction's body ran, procedure_commit_mut_tx failed; the bindings then re-ran the body once and retried the commit. This .expect fires when that second commit also fails - the body deterministically reproduces a commit-breaking condition (a write conflict, a constraint violation, or an I/O error during commit).
Source
Thrown at crates/bindings/src/lib.rs:1230
impl<F: Fn()> Drop for DoOnDrop<F> {
fn drop(&mut self) {
(self.0)();
}
}
let abort_guard = DoOnDrop(abort);
let res = body(&tx);
core::mem::forget(abort_guard);
res
};
let mut res = run();
match res {
Ok(_) if crate::sys::procedure::procedure_commit_mut_tx().is_err() => {
log::warn!("committing anonymous transaction failed");
res = run();
match res {
Ok(_) => crate::sys::procedure::procedure_commit_mut_tx().expect("transaction retry failed again"),
Err(_) => abort(),
}
}
Ok(_) => {}
Err(_) => abort(),
}
res
}
fn with_tx<T>(body: impl Fn(&TxContext) -> T, identity: Identity, connection_id: Option<ConnectionId>) -> T {
use core::convert::Infallible;
match try_with_tx::<T, Infallible>(|tx| Ok(body(tx)), identity, connection_id) {
Ok(v) => v,
Err(e) => match e {},
}
}
View on GitHub (pinned to 524b4487d9)
Solutions
- Inspect host logs for the underlying commit error on both attempts.
- Make the body's writes idempotent and conflict-free: narrow row sets, avoid hot rows, use upsert-style writes.
- Reduce transaction size or split work across multiple commits.
- If host-side I/O is at fault, fix storage before retrying.
Defensive patterns
Strategy: try-catch
Try / catch
let outcome = std::panic::catch_unwind(std::AssertUnwindSafe(|| invoke_procedure()));
if outcome.is_err() {
// Second commit failed too: pull the underlying commit error from host logs,
// fix the conflict or storage issue, then re-invoke the procedure fresh.
} Prevention
- Design procedure bodies to be idempotent and conflict-free on retry.
- Avoid writing hot rows that contend across concurrent transactions.
- Keep transactions small so commits rarely fail.
- Monitor host commit-failure metrics; deterministic failures indicate a bug, not contention.
When it happens
Trigger: A body whose writes conflict with concurrent activity on both attempts (hot rows, contention); disk/IO errors at commit time; oversized transactions; state that is deterministically invalid on every run.
Common situations: High-contention tables causing repeated conflicts; failing storage during commit; bugs where the body always writes an invalid combination of rows.
Related errors
- should have a pending mutable anon tx as `procedure_start_mu
- holding `&mut HandlerContext`, so should not be in a tx alre
- Failed to parse JWT payload
- never types are not yet supported in C# output
- unions not supported
AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16).
Data as JSON: /api/errors/afcc53a8b27133f3.
Report an issue: GitHub.