clockworklabs/SpacetimeDB · error · InvalidOperationException
Transaction context was not initialised.
Error message
Transaction context was not initialised.
What it means
The module runtime keeps per-invocation transaction state (TransactionalContextState): EnterTxContext opens it (called when the host starts a reducer/procedure transaction) and ExitTxContext clears it. RequireTxContext is what generated table/DbContext accessors call to obtain the user-facing transaction context; if txContext is null — i.e. no transaction is active on this thread — it throws this InvalidOperationException. It means module code touched the database outside a host-driven transaction.
Source
Thrown at crates/bindings-csharp/Runtime/TransactionalContextState.cs:51
private Internal.TxContext? txContext;
private TTxContext? cachedUserTxContext;
public Internal.TxContext EnterTxContext(long timestampMicros)
{
var timestamp = new Timestamp(timestampMicros);
Timestamp = timestamp;
txContext = txContext?.WithTimestamp(timestamp) ?? createInitialTxContext(timestamp);
return txContext;
}
public void ExitTxContext() => txContext = null;
public TTxContext RequireTxContext()
{
var inner =
txContext
?? throw new InvalidOperationException("Transaction context was not initialised.");
cachedUserTxContext ??= createTxContext(inner);
cachedUserTxContext.Refresh(inner);
return cachedUserTxContext;
}
public TResult WithTx<TResult>(Func<TTxContext, TResult> body) =>
TryWithTx(tx => Result<TResult, Exception>.Ok(body(tx))).UnwrapOrThrow();
public TxOutcomeCore<TResult> TryWithTx<TResult, TError>(
Func<TTxContext, Result<TResult, TError>> body
)
where TError : Exception
{
try
{
var result = RunWithRetry(body);
return result switchView on GitHub (pinned to 6dee26c6ef)
Solutions
- Move all table access inside [Reducer]/[Procedure] methods so the host enters the transaction first
- For HTTP handlers and other non-transactional entry points, wrap DB work in WithTx(...) / TryWithTx(...) which manage EnterTxContext/ExitTxContext
- Delete seeding/static-init code that touches tables and replace it with an init reducer the host invokes
- Never cache or share Db handles across invocations — always resolve them inside the transactional callback
Example fix
// before - table touched outside any transaction
public static class Module {
static Module() { MyTable.Insert(new Row(1)); } // RequireTxContext throws
}
// after - seed from a lifecycle reducer (or wrap in WithTx)
[SpacetimeDB.Reducer]
public static void Init(ReducerContext ctx) { MyTable.Insert(new Row(1)); } Defensive patterns
Strategy: validation
Try / catch
try { DoTableWork(); }
catch (InvalidOperationException ioe) when (ioe.Message == "Transaction context was not initialised.")
{
// code reached DB access outside a host transaction; move it into a reducer/procedure
// or wrap with WithTx/TryWithTx rather than swallowing it
} Prevention
- Touch tables only inside [Reducer]/[Procedure] bodies or WithTx/TryWithTx callbacks
- No table access in static constructors, field initializers, or background threads
- Resolve Db/table handles per-invocation; never cache them across transactions
- In tests, drive DB code through the host harness rather than calling generated accessors directly
When it happens
Trigger: Calling table accessors (generated Db handles, Iter/Insert/Delete) from module static constructors, static field initializers, or arbitrary methods invoked outside a reducer/procedure callback; running DB logic in an HTTP handler body without going through WithTx/TryWithTx; calling reducer helper code from a background thread or timer.
Common situations: Seeding data at module startup from a constructor instead of a lifecycle reducer; refactoring reducer bodies into shared helpers that are then also called from non-transactional contexts; new HTTP handler features that read tables directly; unit tests invoking generated Db methods without the host harness.
Related errors
- Unknown reducer {reducer}
- Unique index point scan returned >1 rows
- InvalidTableVisibility
- Invalid procedure signature.
- Invalid HTTP handler signature.
AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20).
Data as JSON: /api/errors/dae08c0bba85b881.
Report an issue: GitHub.