clockworklabs/SpacetimeDB · error

view evaluation failed during environment publication

Error message

view evaluation failed during environment publication

What it means

After applying an environment update and its transactional changes, the actor runs evaluate_subscribed_views on the updated instance. If a view traps or its outcome is not Success, the transaction is rolled back and the caller receives UpdateDatabaseResult::ErrorExecutingMigration with this message — the environment change itself was fine, but the module's subscribed views could not be evaluated against it, so publication is treated as a failed migration.

Solutions

  1. Inspect server logs to find which view trapped and fix its logic in the module.
  2. Ensure views only reference environment keys that still exist after the update.
  3. Fix the module, then republish; the failed transaction was rolled back so state is unchanged.

Example fix

// before: view reads a removed env key
let key = ctx.env_get("OLD_KEY").unwrap();

// after: tolerate missing keys
let key = ctx.env_get("OLD_KEY").unwrap_or_default();
Defensive patterns

Strategy: try-catch

Validate before calling

// preflight: ensure every view-referenced env key exists in the new environment
for key in view_env_keys { assert!(new_environment.contains_key(key), "missing env key {key}"); }

Try / catch

match result { UpdateDatabaseResult::ErrorExecutingMigration(e) if e.to_string().contains("view evaluation failed") => inspect_and_fix_views(), _ => {} }

Prevention

When it happens

Trigger: Publishing a database/environment update where at least one subscribed view traps during re-evaluation (panic, error, or non-Success ViewOutcome) after the environment rows were replaced.

Common situations: A view reads an environment key that was removed or renamed in the new environment; a buggy view reducer that panics on the new data; views incompatible with the newly published module.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@eddf9f5014 (2026-09-20). Data as JSON: /api/errors/90279bb162504de3. Report an issue: GitHub.

Appendix: source

Thrown at crates/core/src/host/wasm_common/module_host_actor.rs:849

        let tx = db.begin_mut_tx(IsolationLevel::Serializable, Workload::Internal);
        let (tx, _) = db.with_auto_rollback(tx, |tx| -> anyhow::Result<()> {
            use spacetimedb_datastore::system_tables::{read_hash_from_col, StModuleFields, ST_MODULE_ID};
            let row = tx
                .iter(ST_MODULE_ID)?
                .next()
                .context("database program is not initialized")?;
            anyhow::ensure!(
                read_hash_from_col(row, StModuleFields::ProgramHash)? == self.info.module_hash,
                "database program changed before publication"
            );
            crate::db::environment::replace(db, tx, self.info.module_def.environment(), &environment)?;
            Ok(())
        })?;
        let (out, _, trapped) = self.evaluate_subscribed_views(tx, inst)?;
        if trapped || out.outcome != ViewOutcome::Success {
            let (_, metrics, reducer) = db.rollback_mut_tx(out.tx);
            db.report_mut_tx_metrics(reducer, metrics, None);
            return Ok(UpdateDatabaseResult::ErrorExecutingMigration(anyhow::anyhow!(
                "view evaluation failed during environment publication"
            )));
        }
        let event = ModuleEvent {
            timestamp: Timestamp::now(),
            caller_identity: self.info.owner_identity,
            caller_connection_id: None,
            function_call: ModuleFunctionCall::update(),
            status: EventStatus::Committed(DatabaseUpdate::default()),
            reducer_return_value: None,
            execution_budget_used: out.execution_budget_used,
            host_execution_duration: out.total_duration,
            request_id: None,
            timer: None,
        };
        let durable_offset = db.durable_tx_offset();
        let CommitAndBroadcastEventSuccess { tx_offset, .. } =
            commit_and_broadcast_event(&self.info.subscriptions, None, event, out.tx);

View on GitHub (pinned to eddf9f5014)