clockworklabs/SpacetimeDB · error
ERROR: Constraint registration failed [%s] %s
Error message
ERROR: Constraint registration failed [%s] %s
What it means
Generic constraint-registration failure reported by SetConstraintRegistrationError(code, details). V10Builder calls it with a machine-readable code for many schema mistakes: TABLE_NO_FIELD_DESCRIPTORS, NO_FIELD_DESCRIPTORS, FIELD_NOT_FOUND, TABLE_NOT_FOUND, MULTI_INDEX_EMPTY, DEFAULT_ON_PRIMARY_KEY and others (see include/spacetimedb/internal/v10_builder.h). The flag makes __preinit__99_validate_types replace the module with error type ERROR_CONSTRAINT_REGISTRATION_<code>, so the publish fails with that name on the server.
Source
Thrown at crates/bindings-cpp/src/internal/Module.cpp:93
static std::string g_constraint_registration_error_code = "";
static std::string g_constraint_registration_error_details = "";
// External global flags for circular reference detection (defined in module_type_registration.cpp)
extern bool g_circular_ref_error;
extern std::string g_circular_ref_type_name;
// Function to set the multiple primary key error flag
void SetMultiplePrimaryKeyError(const std::string& table_name) {
g_multiple_primary_key_error = true;
g_multiple_primary_key_table_name = table_name;
fprintf(stderr, "ERROR: Multiple primary keys detected in table '%s'\n", table_name.c_str());
}
void SetConstraintRegistrationError(const std::string& code, const std::string& details) {
g_constraint_registration_error = true;
g_constraint_registration_error_code = code;
g_constraint_registration_error_details = details;
fprintf(stderr, "ERROR: Constraint registration failed [%s] %s\n", code.c_str(), details.c_str());
}
// Register a reducer handler (called by V9Builder during registration)
void RegisterReducerHandler(const std::string& name,
std::function<void(ReducerContext&, BytesSource)> handler,
std::optional<Lifecycle> lifecycle) {
g_reducer_handlers.push_back({name, handler, lifecycle});
}
// Register a view handler (called by V9Builder during registration)
void RegisterViewHandler(const std::string& name,
std::function<std::vector<uint8_t>(ViewContext&, BytesSource)> handler) {
g_view_handlers.push_back({name, handler});
}
// Register an anonymous view handler (called by V9Builder during registration)
void RegisterAnonymousViewHandler(const std::string& name,
std::function<std::vector<uint8_t>(AnonymousViewContext&, BytesSource)> handler) {View on GitHub (pinned to 6dee26c6ef)
Solutions
- Read the bracketed code and details string: FIELD_NOT_FOUND -> correct the field name in the constraint/index macro to match the struct member
- TABLE_NOT_FOUND -> ensure the table registration macro for that table is compiled before/with the constraint macros (same header or TU)
- MULTI_INDEX_EMPTY -> give the multi-column index at least one column name
- NO_FIELD_DESCRIPTORS / TABLE_NO_FIELD_DESCRIPTORS -> make sure the table struct uses the SpacetimeDB table registration so field_registrar<T>::register_fields() populates descriptors
- DEFAULT_ON_PRIMARY_KEY -> remove the column default from the primary-key column
Example fix
// before
struct User { uint64_t user_id; std::string name; };
FIELD_Unique(User, usr_id); // typo -> FIELD_NOT_FOUND
// after
FIELD_Unique(User, user_id); Defensive patterns
Strategy: validation
Validate before calling
// compile-time existence check next to each constraint macro: a typo'd
// member pointer fails to build before publish
static_assert(&User::user_id != nullptr,
"User.user_id must exist for FIELD_Unique(User, user_id)"); Prevention
- Put FIELD_* and index macros directly under the struct they constrain, in the same translation unit
- Rename fields and macros together in one change; let the compiler's member-pointer checks catch typos
- Publish to a dev database in CI so constraint codes surface before production
When it happens
Trigger: A FIELD_Unique/FIELD_Index macro naming a field that is not in the table's registered field descriptors (FIELD_NOT_FOUND); a multi-column index registered with an empty field list (MULTI_INDEX_EMPTY); a constraint applied before the table itself is registered (TABLE_NOT_FOUND); a table type whose field_registrar<T> produced no descriptors; a column default applied to the primary-key column (DEFAULT_ON_PRIMARY_KEY).
Common situations: Renaming a struct member without updating the adjacent FIELD_* macro; splitting table and constraint macros across translation units so preinit ordering registers constraints first; adding defaults to key columns when copying an ORM schema.
Related errors
- [CONSTRAINT REGISTRATION ERROR] Module cleared and replaced
- Original error: %s
- table should exist in the database for AddConstraint
- ERROR: Multiple primary keys detected in table '%s'
- [CIRCULAR REFERENCE ERROR] Module cleared and replaced with
AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20).
Data as JSON: /api/errors/7b1d39af5168e78d.
Report an issue: GitHub.