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
- Check every field string in the multi-index list against the struct's member names, case-sensitively
- After member renames, update all index/constraint/default annotations that mention the old name
- Confirm all listed fields belong to the row type passed as T
- 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
- Update index column lists in the same change as member renames
- Prefer compile-time coupling (macros reading members) over free-form name strings where the bindings allow
- Assert published indexes match expectations in a post-publish check
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
- ERROR: Field '%s' not found in table '%s'
- ERROR: Skipping multi-column index registration '%s.%s' beca
- ERROR: Table '%s' not found for multi-column index '%s'
- ERROR: Skipping default-value registration '%s.%s' because c
- ERROR: Skipping reducer registration '%s' because circular r
AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16).
Data as JSON: /api/errors/710b6f9990df8f0f.
Report an issue: GitHub.