clockworklabs/SpacetimeDB · error

ERROR: No field descriptors found for table %s

Error message

ERROR: No field descriptors found for table %s

What it means

After AddFieldConstraint calls SpacetimeDB::field_registrar<T>::register_fields(), it looks up the field descriptor map by &typeid(T). If T never produced descriptors, the lookup fails and the constraint is dropped with this message. In practice it means the template type T used by the constraint macro is not (the same type identity as) the row type whose fields were registered for the table.

Source

Thrown at crates/bindings-cpp/include/spacetimedb/internal/v9_builder.h:447

void V9Builder::AddFieldConstraint(const std::string& table_name,
                                   const std::string& field_name,
                                   FieldConstraint constraint) {
    // AddFieldConstraint implementation
    
    // Find the existing table by name
    RawTableDefV9* table = findTableByName(table_name);
    if (!table) {
        fprintf(stderr, "ERROR: Table '%s' not found when trying to add constraint to field '%s'\n",
                table_name.c_str(), field_name.c_str());
        return;
    }
    
    // Get field descriptors to find the field index
    SpacetimeDB::field_registrar<T>::register_fields();
    auto& descriptor_map = SpacetimeDB::get_table_descriptors();
    auto it = descriptor_map.find(&typeid(T));
    if (it == descriptor_map.end()) {
        fprintf(stderr, "ERROR: No field descriptors found for table %s\n", table_name.c_str());
        return;
    }
    
    const auto& field_descs = it->second.fields;
    uint16_t field_idx = 0;
    bool field_found = false;
    
    // Find the field index
    for (const auto& field_desc : field_descs) {
        if (field_desc.name == field_name) {
            field_found = true;
            break;
        }
        field_idx++;
    }
    
    if (!field_found) {
        fprintf(stderr, "ERROR: Field '%s' not found in table '%s'\n", 

View on GitHub (pinned to 524b4487d9)

Solutions

  1. Ensure the constraint macro receives the exact row struct type that the table macro used, spelled identically in both places
  2. Confirm the row struct contains its field registration macro (field_registrar specialization) so register_fields populates descriptors
  3. Avoid typedef/alias indirection for row types in constraint registrations
  4. If the table is templated, check that no cv/reference qualifications leak into T before the typeid lookup

Example fix

// before — constraint registered for a different type identity
using UserRow = User;
ST_UNIQUE_ON_TYPE(UserRow, "users", id);   // typeid(UserRow) may not match
// after
ST_UNIQUE_ON_TYPE(User, "users", id);       // same type as the table registration
Defensive patterns

Strategy: validation

Validate before calling

// Assert descriptors exist for the exact row type before/after registration:
#include <spacetimedb/spacetimedb.h> // provides get_table_descriptors
bool HasDescriptorsFor(const std::type_index& ti) {
    return SpacetimeDB::get_table_descriptors().count(ti.operator()) != 0; // adapt to API shape
}

Prevention

When it happens

Trigger: Passing a wrapper/alias/base type instead of the actual row struct to the constraint macro; register_fields registering descriptors under a different type identity than typeid(T> (cv-qualified or typedef-diverged instantiations); the row type's field_registrar specialization missing because the type lacks the required field registration macro.

Common situations: Using a typedef or using-alias for the row type in one file so typeid differs; templated table helpers where T is deduced as a reference/const-qualified variant; splitting table definition and constraint macros across headers compiled with different type spellings; forgetting the field-registration macro inside the struct.

Related errors


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