clockworklabs/SpacetimeDB · error

ERROR: Field '%s' not found in table '%s' for multi-column i

Error message

ERROR: Field '%s' not found in table '%s' for multi-column index

What it means

AddMultiColumnIndex walked the row type's field descriptors but one of the names in field_names matched no field_desc.name, so it aborts the whole index (return, not partial). Every listed column must resolve to a descriptor index before the composite btree is built; one bad name means no index at all.

Source

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

    std::vector<uint16_t> field_indexes;
    
    // Find field indices for each field name
    for (const std::string& field_name : field_names) {
        uint16_t field_idx = 0;
        bool field_found = false;
        
        for (const auto& field_desc : field_descs) {
            if (field_desc.name == field_name) {
                field_indexes.push_back(field_idx);
                field_found = true;
                //fprintf(stdout, "DEBUG: Field '%s' -> index %u\n", field_name.c_str(), field_idx);
                break;
            }
            field_idx++;
        }
        
        if (!field_found) {
            fprintf(stderr, "ERROR: Field '%s' not found in table '%s' for multi-column index\n",
                    field_name.c_str(), table_name.c_str());
            return;
        }
    }
    
    // Create the multi-column BTree algorithm
    RawIndexAlgorithmBTreeData btree_data;
    btree_data.columns = field_indexes;
    
    RawIndexAlgorithm algorithm;
    algorithm.set<0>(btree_data);  // Set BTree variant
    
    // Create the index definition with both the user-provided name and generated btree name
    RawIndexDefV9 index_def;
    std::string generated_name = table_name + "_" + field_names[0];
    for (size_t i = 1; i < field_names.size(); ++i) {
        generated_name += "_" + field_names[i];
    }

View on GitHub (pinned to 524b4487d9)

Solutions

  1. Check every field string in the multi-index list against the struct's member names, case-sensitively
  2. After member renames, update all index/constraint/default annotations that mention the old name
  3. Confirm all listed fields belong to the row type passed as T
  4. Republish and confirm the index (generated name '<f0>_<f1>_idx_btree') exists

Example fix

// before
struct Message { uint32_t sender; uint32_t room_id; };
ST_MULTI_INDEX(message, by_sender_room, sender_id, room_id);  // 'sender_id' unknown
// after
ST_MULTI_INDEX(message, by_sender_room, sender, room_id);
Defensive patterns

Strategy: validation

Validate before calling

// Validate every listed column resolves before shipping (after init, row type T):
SpacetimeDB::field_registrar<T>::register_fields();
const auto& fields = SpacetimeDB::get_table_descriptors()[&typeid(T)].fields;
for (const char* col : {"sender_id", "room_id"}) {
    bool ok = std::any_of(fields.begin(), fields.end(),
        [&](const auto& f){ return f.name == col; });
    if (!ok) /* fail the build/test */;
}

Prevention

When it happens

Trigger: Any column string in the multi-column index list that doesn't exactly match a registered field name: renamed members, casing differences, listing a column from a different struct, typos introduced when splitting the index across lines.

Common situations: Renaming one of the indexed columns during a refactor without updating the index annotation; mixing DB column names and C++ member names; adding a new column to the index before adding it to the struct.

Related errors


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