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

  1. Inspect error.cause: it carries the real sys error that made both commits fail
  2. If the cause is deterministic (constraint or budget violation), fix the procedure body; a blind retry cannot succeed
  3. If the cause looks transient/host-level, check spacetimedb host logs and resource limits for the instance
  4. 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

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


AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16). Data as JSON: /api/errors/4df230e9e01e5cd7. Report an issue: GitHub.