clockworklabs/SpacetimeDB · error

view {} not found in module_def

Error message

view {} not found in module_def

What it means

When (re)initializing views during publish/update, the host computes each view's full namespaced canonical name (prefix joined with the local name, matching how create_view registered it) and looks it up in module_def via view_by_name_with_global_fn_ptr. A miss means the database expects a view the current module does not define under that exact name. Because the name includes the namespace prefix, moving a view between submodules changes it.

Source

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

    owner_identity: Identity,
) -> Result<Vec<CallViewParams>, anyhow::Error> {
    let mut view_calls = Vec::new();

    for (prefix, owning_def, view) in module_def.all_views_with_prefix() {
        let ViewDef {
            name: local_name,
            is_anonymous,
            product_type_ref,
            ..
        } = view;

        // Full namespaced canonical name: matches both the st_view registration
        // (create_view / create_view_with_prefix) and `view_by_name_with_global_fn_ptr`.
        let view_name = prefix.join(local_name.clone());

        let (global_fn_ptr, _, _) = module_def
            .view_by_name_with_global_fn_ptr(&view_name)
            .ok_or_else(|| anyhow::anyhow!("view {} not found in module_def", view_name))?;

        let st_view = tx
            .view_from_name(&view_name)?
            .ok_or_else(|| anyhow::anyhow!("view {} not found in database", view_name))?;

        let view_id = st_view.view_id;
        let table_id = st_view
            .table_id
            .ok_or_else(|| anyhow::anyhow!("view {} does not have a backing table in database", view_name))?;
        let subs = tx.materialized_view_instances_for_view(view_id);
        let view_typespace = Arc::new(owning_def.typespace().clone());

        if *is_anonymous {
            if subs.is_empty() {
                continue;
            }
            view_calls.push(CallViewParams {
                view_name: view_name.clone(),

View on GitHub (pinned to 524b4487d9)

Solutions

  1. Check the deployed module's view list and make the canonical name (including namespace prefix) match on both sides.
  2. If the view was renamed intentionally, remove the stale registration (drop the view, or recreate the database if disposable) and republish.
  3. Republish the module that defines the view under the name the database expects.
  4. After renames, verify the view catalog is consistent before relying on materialized state.
Defensive patterns

Strategy: validation

Validate before calling

// before publish: canonical view names must exist in both module and database catalog
let module_views: HashSet<_> = module_def.view_names().collect();
let db_views: HashSet<_> = tx.view_names()?;
for name in module_views.symmetric_difference(&db_views) {
    anyhow::bail!("view catalog mismatch: {name}");
}

Prevention

When it happens

Trigger: Renaming or moving a view between publishes so an old st_view registration survives under the previous name; publishing a module revision that dropped a view still registered in the database; submodule reorganization changing the prefixed canonical name.

Common situations: Iterative development where views are renamed or deleted across publishes; cleanups that update module code but not the database's view catalog; multi-module layouts with changing namespaces.

Related errors


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