clockworklabs/SpacetimeDB · error
ERROR: Invalid type index %u for namespace update (max: %zu)
Error message
ERROR: Invalid type index %u for namespace update (max: %zu)\n
What it means
Printed by ModuleTypeRegistration::updateTypeNameInModule when the type index passed in is >= GetTypeDefs().size(). The index comes from type_name_cache_, populated by registerType()/registerAndGetIndex() with typespace indices, but this function indexes the RawTypeDefV10 array; the update is then silently skipped, so the type keeps its un-namespace-qualified name. It is called from set_type_namespace<T>() in module_type_registration.h:151, which the SPACETIMEDB_NAMESPACE macros invoke during preinit to prepend a namespace to an already-registered type.
Source
Thrown at crates/bindings-cpp/src/internal/module_type_registration.cpp:632
}
desc += "}";
return desc;
}
case bsatn::AlgebraicTypeTag::Ref:
return "Ref(" + std::to_string(type.as_ref()) + ")";
default:
return "Unknown(tag=" + std::to_string(static_cast<int>(type.tag())) + ")";
}
}
void ModuleTypeRegistration::updateTypeNameInModule(uint32_t type_index, const std::string& new_name) {
auto& type_defs = getV10Builder().GetTypeDefs();
// Check if the type index is valid
if (type_index >= type_defs.size()) {
fprintf(stderr, "ERROR: Invalid type index %u for namespace update (max: %zu)\n",
type_index, type_defs.size());
return;
}
// Parse the new name to extract namespace and name parts
auto [scope, name] = parseNamespaceAndName(new_name);
// Update the type definition's scoped name
type_defs[type_index].source_name.scope = scope;
type_defs[type_index].source_name.source_name = name;
}
// processOptionInnerType function removed - no longer needed
// Options now use the same LazyTypeRegistrar pattern as other types
} // namespace Internal
View on GitHub (pinned to 6dee26c6ef)
Solutions
- Ensure every rebuild path calls ModuleTypeRegistration::clear() and resets the V10 builder's typespace and type-def arrays together, so cached indices never outlive the arrays they index (see initializeModuleTypeRegistration at module_type_registration.cpp:28).
- Audit for typespace pushes without a matching GetTypeDefs() push: registerComplexType() pushes both (lines 488-499); any other code pushing into GetTypespace().types directly must push a RawTypeDefV10 too, or indices diverge.
- Prefer a name-keyed update: change set_type_namespace/updateTypeNameInModule to locate the type-def by source_name instead of by cached index, which is immune to index divergence.
- As a guard, bound-check the index at the call site (module_type_registration.h:151) and fail loudly during preinit instead of silently skipping the namespace qualification.
Example fix
// before (module_type_registration.h, set_type_namespace): // cached typespace index is used to index the type-def array updateTypeNameInModule(type_index, qualified_name); // after: resolve the type-def by name so typespace/type-def index // divergence cannot produce an out-of-range index updateTypeNameInModule(original_name, qualified_name); // (updateTypeNameInModule finds the def via source_name, then rewrites scope/name)
Defensive patterns
Strategy: validation
Validate before calling
// in set_type_namespace (module_type_registration.h), before updating:
const auto& defs = getV10Builder().GetTypeDefs();
if (type_index >= defs.size()) {
// typespace/type-def index divergence or stale cache: do not silently skip
fprintf(stderr, "namespace update for %s skipped: stale index %u (defs: %zu)\n",
original_name.c_str(), type_index, defs.size());
return;
} Prevention
- Register types and apply SPACETIMEDB_NAMESPACE macros in the same preinit pass; never apply namespace macros against a cache from an earlier build.
- On every in-process rebuild, call ModuleTypeRegistration::clear() and reset the V10 builder's typespace and type-def arrays together so cached indices cannot outlive the arrays.
- Never push entries into GetTypespace().types directly without a matching RawTypeDefV10 push - that is what makes indices diverge.
- After module build, assert every published type name carries its expected namespace prefix; a missing prefix is this error's silent symptom.
When it happens
Trigger: A SPACETIMEDB_NAMESPACE(prefix) macro runs for a type whose cached index no longer matches the type-defs array: typespace and type-def arrays have diverged (typespace entries pushed without a matching RawTypeDefV10), a stale type_name_cache_ left over from a previous module build because clear() was not called while the builder was reset (or vice versa), or any path where the typespace grows faster than GetTypeDefs(). Concretely: registering types directly into the typespace (v10_builder pushes) and then using namespace macros on them, or rebuilding a module in the same process without ModuleTypeRegistration::clear().
Common situations: Unit tests or host tooling that build a module, reset state, and rebuild in one process; mixed registration styles (macro-based registration plus manual typespace pushes); version transitions of bindings-cpp where the cache/type-def bookkeeping changed shape. The visible symptom is the namespace prefix missing from the published schema (type names without 'Namespace.') plus this stderr line.
Related errors
- [Warning] Unexpected type tag %d for '%s'\n
- ERROR: Table '%s' did not register as a complex type\n
- AddConstraint: constraint `{constraint_name}` not found in n
- AddSequence: sequence `{sequence_name}` not found in new mod
- Router registration failed: %s
AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20).
Data as JSON: /api/errors/182249ad991e717f.
Report an issue: GitHub.