clockworklabs/SpacetimeDB · critical · Error
transaction retry failed again
Error message
transaction retry failed again
What it means
Procedures run inside anonymous transactions via runWithTx: the body executes, then sys.procedure_commit_mut_tx() commits. If the first commit throws, the runtime logs 'committing anonymous transaction failed', replays the whole transaction once, and commits again; a second commit failure throws 'transaction retry failed again' with the underlying host error attached as `cause`.
Source
Thrown at crates/bindings-typescript/src/server/runtime.ts:367
sys.procedure_abort_mut_tx();
throw e;
}
};
let res = run();
try {
sys.procedure_commit_mut_tx();
return res;
} catch {
// ignore the commit error
}
console.warn('committing anonymous transaction failed');
res = run();
try {
sys.procedure_commit_mut_tx();
return res;
} catch (e) {
throw new Error('transaction retry failed again', { cause: e });
}
}
type FlatSubmoduleDispatch = {
reducerFns: Reducers;
reducerDefs: RawReducerDefV10[];
procedureFns: Procedures;
procedureDefs: RawProcedureDefV10[];
anonViewFns: AnonViews;
viewFns: Views;
tables: Array<{ accessorName: string; tableDef: RawTableDefV10 }>;
schemaTables: Record<string, UntypedTableDef>;
typespace: Typespace;
dbView_: DbView<any> | undefined;
queryBuilder_: QueryBuilder<any> | undefined;
/** e.g. "alias." for a submodule with namespace alias "alias" */
namePrefix: string;
subDispatches: SubmoduleDispatchInfo[];View on GitHub (pinned to 524b4487d9)
Solutions
- Inspect error.cause: it carries the real sys error that made both commits fail
- If the cause is deterministic (constraint or budget violation), fix the procedure body; a blind retry cannot succeed
- If the cause looks transient/host-level, check spacetimedb host logs and resource limits for the instance
- If the data should legitimately commit, report an issue with the cause attached and the module hash
Defensive patterns
Strategy: try-catch
Try / catch
try {
await runProcedure();
} catch (e) {
const cause = (e as Error)?.cause ?? e;
if (isTransientHostError(cause)) {
scheduleRetryWithBackoff(); // procedure must be idempotent
} else {
logAndAlert('procedure commit failed', { cause });
}
} Prevention
- Always inspect and log error.cause; the outer message alone hides the real failure
- Keep procedure writes within host constraints and budgets; deterministic failures cannot be retried away
- Make procedures idempotent so an external retry after a failed commit is safe
- Watch for the 'committing anonymous transaction failed' warning in logs as an early signal
When it happens
Trigger: Any host-side commit error occurring twice in a row: a datastore error at commit time, resource/budget exhaustion, or a deterministic failure caused by the procedure's own writes (a deterministically failing write fails identically on replay).
Common situations: Module under load hitting host limits; writes that violate host-level constraints surfacing only at commit; transient host storage issues where the single built-in retry is not enough.
Related errors
- unknown procedureId ${id}
- wrong number of elements
- too many elements
- cannot serialize refs without a typespace
- cannot deserialize refs without a typespace
AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16).
Data as JSON: /api/errors/4df230e9e01e5cd7.
Report an issue: GitHub.