clockworklabs/SpacetimeDB · error · anyhow::Error

module definition is unavailable while committing a procedur

Error message

module definition is unavailable while committing a procedure transaction

What it means

During WasmInstanceEnv::refresh_views, which runs while committing a procedure (reducer) transaction, caller.data().module_def was None. The instance env must hold the module definition to resolve views for refresh; its absence means instance state was cleared (e.g. during module update or teardown) while a procedure transaction was still committing. The transaction is rolled back and the error returned.

Source

Thrown at crates/core/src/host/wasmtime/wasm_instance_env.rs:1759

                    .commit_procedure_tx(tx)
                    .map_err(WasmError::from)?;
                Ok(0u16.into())
            })();
            res.or_else(|err| Self::convert_wasm_result(AbiCall::ProcedureCommitMutTransaction, err))
        })
    }

    /// Refresh all views made stale by a procedure `tx`.
    ///
    /// This runs each pending view call in the same mutable transaction and writes the refreshed rows
    /// into the corresponding backing view tables. If any step fails (missing metadata, view execution,
    /// row decoding, SQL execution, or materialization), this method rolls back `tx` and returns an error.
    ///
    /// On success, it returns the same transaction handle so the caller can commit it.
    fn refresh_views<'a>(caller: &mut Caller<'a, Self>, tx: MutTxId) -> Result<MutTxId, WasmError> {
        let Some(module_def) = caller.data().module_def.clone() else {
            caller.data_mut().instance_env.rollback_procedure_tx(tx);
            return Err(WasmError::Wasm(anyhow!(
                "module definition is unavailable while committing a procedure transaction"
            )));
        };

        let views_for_refresh = tx.views_for_refresh().cloned().collect::<Vec<_>>();
        let mut tx = Some(tx);
        let mut tx_slot = caller.data().instance_env.tx.clone();

        for view_call in views_for_refresh {
            let res: anyhow::Result<()> = (|| {
                let resolved = crate::host::module_host::resolve_view_for_refresh(
                    tx.as_ref().expect("procedure tx missing during view refresh"),
                    &module_def,
                    &view_call,
                )?;

                let table_id = resolved.table_id;
                let view_def = resolved.view_def;

View on GitHub (pinned to 6dee26c6ef)

Solutions

  1. Retry the reducer call once the module update completes - the transaction was rolled back safely, so no partial state was written
  2. Drain or quiesce reducer traffic before publishing module updates
  3. If it recurs outside update windows, capture server logs and report it - module_def should never be missing mid-commit
Defensive patterns

Strategy: retry

Try / catch

match commit_result {
    Err(e) if e.to_string().contains("module definition is unavailable") => {
        // Tx was rolled back safely: wait for the module update to settle, then retry the reducer once
    }
    other => other,
}

Prevention

When it happens

Trigger: A module update or instance teardown racing with an in-flight reducer commit on the wasmtime instance: the module def is swapped out before commit_procedure_tx finishes.

Common situations: Publishing a new module version while reducers are executing under load; node restarts during hot-swap; lifecycle bugs in the host.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20). Data as JSON: /api/errors/88fa797debb9bdc9. Report an issue: GitHub.