clockworklabs/SpacetimeDB · error

expected ModuleDef to contain key, but it does not

Error message

expected ModuleDef to contain key, but it does not

What it means

`ModuleDef::lookup_expect` is the panicking variant of `lookup`: it resolves a table/view/reducer/index by key and `.expect`s the result, reserved for cases where presence is guaranteed by construction. It panics when the key is absent from the loaded module definition — wrong name, renamed entity, or an id from a stale module build.

Source

Thrown at crates/schema/src/def.rs:571

    /// The `TableDef` an entity in the global namespace is stored in, if any.
    ///
    /// Generally, you will want to use the `lookup` method on the entity type instead.
    pub fn stored_in_table_def(&self, name: &RawIdentifier) -> Option<&TableDef> {
        self.stored_in_table_def
            .get(name)
            .and_then(|table_name| self.tables.get(table_name))
    }

    /// Lookup a definition by its key in `self`.
    pub fn lookup<T: ModuleDefLookup>(&self, key: T::Key<'_>) -> Option<&T> {
        T::lookup(self, key)
    }

    /// Lookup a definition by its key in `self`, panicking if not found.
    /// Only use this method if you are sure the key exists in the module definition.
    pub fn lookup_expect<T: ModuleDefLookup>(&self, key: T::Key<'_>) -> &T {
        T::lookup(self, key).expect("expected ModuleDef to contain key, but it does not")
    }

    /// Convenience method to look up a table, possibly by a string.
    pub fn table<K: ?Sized + Hash + Equivalent<Identifier>>(&self, name: &K) -> Option<&TableDef> {
        // If the string IS a valid identifier, we can just look it up.
        self.tables.get(name)
    }

    /// Convenience method to look up a view, possibly by a string.
    pub fn view<K: ?Sized + Hash + Equivalent<Identifier>>(&self, name: &K) -> Option<&ViewDef> {
        // If the string IS a valid identifier, we can just look it up.
        self.views.get(name)
    }

    /// Convenience method to look up a view, possibly by a string, returning its id as well.
    pub fn view_full<K: ?Sized + Hash + Equivalent<Identifier>>(&self, name: &K) -> Option<(ViewFnPtr, &ViewDef)> {
        // If the string IS a valid identifier, we can just look it up.
        self.views.get(name).map(|def| (def.fn_ptr, def))

View on GitHub (pinned to 524b4487d9)

Solutions

  1. Switch to the non-panicking `lookup(key)` and turn None into a proper error when presence is not statically guaranteed.
  2. Regenerate bindings/ModuleDef artifacts so names and ids match the current published module.
  3. Log the missing key alongside the set of known keys to spot renames and id drift quickly.

Example fix

// before
let table = module_def.lookup_expect("players");

// after
let table = module_def.lookup("players")
    .ok_or_else(|| anyhow::anyhow!("table `players` not found in module"))?;
Defensive patterns

Strategy: validation

Validate before calling

if module_def.lookup(key).is_none() {
    return Err(format!("key {:?} not found in module def", key).into());
}
let def = module_def.lookup_expect(key);

Prevention

When it happens

Trigger: Calling lookup_expect("table_name") or lookup_expect(table_id) with a key that doesn't exist in this ModuleDef — host code holding ids/names from an older module build, codegen referring to renamed entities, or programmatic lookups built from user input.

Common situations: Version skew between a module build and host caches/bindings; an entity renamed without regenerating bindings; user-supplied table names passed straight to lookup_expect; tests running against outdated fixture defs.

Related errors


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