clockworklabs/SpacetimeDB · error

ERROR: Table '%s' did not register as a complex type\n

Error message

ERROR: Table '%s' did not register as a complex type\n

What it means

Printed by V9Builder::AddV9Table when the AlgebraicType returned by registerType() for a table's row type is not a Ref, i.e. it was not registered into the typespace as a named complex type. Tables must map to a typespace entry referenced by index; on this path the code logs the error and falls back to type_ref = 0, so the table definition points at whatever type occupies index 0 (or nothing), producing a broken schema. registerType() returns a non-Ref exactly when the row type was classified as an inlined kind (primitive/array/unit/special/option/result/ScheduleAt) or when an error path returned a dummy U8 (missing type name at module_type_registration.cpp:126-141, recursive-type detection at lines 143-152).

Source

Thrown at crates/bindings-cpp/src/internal/v9_builder.cpp:101

                                  const std::type_info* cpp_type,
                                  bool is_public,
                                  const std::vector<uint16_t>& primary_key,
                                  const std::vector<RawIndexDefV9>& indexes,
                                  const std::vector<RawConstraintDefV9>& constraints,
                                  const std::vector<RawSequenceDefV9>& sequences,
                                  const std::optional<RawScheduleDefV9>& schedule) {
    
    // Register the table type using the unified system
    // Use empty string to let the system extract the struct name from cpp_type
    AlgebraicType registered_type = registerType(table_type, "", cpp_type);
    
    // Extract the typespace index from the registered type
    uint32_t type_ref;
    if (registered_type.get_tag() == AlgebraicType::Tag::Ref) {
        type_ref = registered_type.get<0>();
    } else {
        // This shouldn't happen for a table type - tables should always be complex types
        fprintf(stderr, "ERROR: Table '%s' did not register as a complex type\n", table_name.c_str());
        type_ref = 0;
    }
    
    // RegisterTable now handles all constraint and index generation,
    // so we just use what was passed in directly
    
    // Create the table definition
    RawTableDefV9 table_def;
    table_def.name = table_name;
    table_def.product_type_ref = type_ref;
    table_def.primary_key = primary_key;
    table_def.indexes = indexes;  // Use indexes passed from RegisterTable
    table_def.constraints = constraints;  // Use constraints passed from RegisterTable
    table_def.sequences = sequences;
    table_def.schedule = schedule;
    table_def.table_type = TableType::User;  // User-defined table
    table_def.table_access = is_public ? TableAccess::Public : TableAccess::Private;
    

View on GitHub (pinned to 6dee26c6ef)

Solutions

  1. Check the registration error state right before this line: ModuleTypeRegistration's error_message_/has_error_ (set at module_type_registration.cpp:128-137 and 143-152) states the real cause - fix that first (give the row type a resolvable name, or remove the recursion).
  2. Declare tables through the SpacetimeDB table macro (SPACETIMEDB_TABLE) so the row type is a top-level named struct and AddV9Table receives a properly named complex type.
  3. Ensure the row type is a named struct/enum, not a primitive, array, std::optional-wrapped type, or a two-variant Interval/Time sum (ScheduleAt look-alike), all of which registerType() inlines and returns non-Ref.
  4. On toolchains without __cxa_demangle, pass an explicit type name instead of relying on typeid demangling (see the SPACETIMEDB_HAS_CXA_DEMANGLE branch at line 129).
  5. After fixing, confirm registered_type.get_tag() == AlgebraicType::Tag::Ref and the emitted product_type_ref matches the row type's typespace index before publishing.

Example fix

// before: row type registers as a non-complex/inlined type, so registerType()
// returns e.g. a dummy U8 and AddV9Table falls into the error branch
// (table_def.product_type_ref = 0 -> broken schema)
struct Row; // never registered as a named complex type

// after: declare the table via the macro so the named struct is registered
// into the typespace and registerType() returns a Ref
#include "spacetimedb/table.hpp"
struct Row { uint64_t id; std::string name; };
SPACETIMEDB_TABLE(Row, public);
Defensive patterns

Strategy: type-guard

Validate before calling

// before AddV9Table / RegisterTable, confirm the row type registers as a
// typespace entry and no registration error is pending:
AlgebraicType probe = registerType(bsatn_row_type, "", &typeid(Row));
if (probe.get_tag() != AlgebraicType::Tag::Ref) {
    // report at preinit: row type is inlined or unnamed (check
    // getModuleTypeRegistration().error_message_ for the root cause)
    report_and_abort("table row type did not register as a complex type");
}

Type guard

bool isComplexTypeRef(const AlgebraicType& t) {
    return t.get_tag() == AlgebraicType::Tag::Ref;
}

Prevention

When it happens

Trigger: Calling AddV9Table/RegisterTable with a row type that is not a named struct or enum: a primitive-only 'table' type, a row type shaped like a special type or ScheduleAt sum that gets inlined, a row type whose name cannot be resolved (empty explicit_name plus a cpp_type that fails extractTypeName/demangling, or a toolchain without __cxa_demangle), or a row type that references itself so cycle detection returns the dummy U8. The fprintf at v9_builder.cpp:101 fires with the table name and product_type_ref is set to 0.

Common situations: Declaring a table without the SpacetimeDB table macro so the row type never gets a registered name; row types defined as typedefs/aliases or anonymous structs whose typeid does not demangle to a usable name; recursive row structs (e.g. containing an Option of themselves) tripping the cycle-detection dummy return; building with a toolchain lacking demangling support so 'Missing type name for complex type' is set on the registration object right before this error. Downstream symptom: publish/validation fails or the module schema references the wrong type at index 0, and a companion message ('Missing type name for complex type: ...' or 'Recursive type reference detected: ...') names the root cause.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20). Data as JSON: /api/errors/23838e9fe7a83f7c. Report an issue: GitHub.