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
- Switch to the non-panicking `lookup(key)` and turn None into a proper error when presence is not statically guaranteed.
- Regenerate bindings/ModuleDef artifacts so names and ids match the current published module.
- 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
- Prefer lookup() plus ok_or_else outside provably-invariant paths.
- Regenerate bindings after module changes; assert expected entity names at startup.
- Log the missing key with the known keys to catch renames and id drift.
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
- never types are not yet supported in C# output
- recursive types not supported
- source_name should be provided in V10, accessor_names inside
- cannot serialize refs without a typespace
- cannot deserialize refs without a typespace
AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16).
Data as JSON: /api/errors/72c89854d8353758.
Report an issue: GitHub.