clockworklabs/SpacetimeDB · error
ERROR: Multiple primary keys detected in table '%s'
Error message
ERROR: Multiple primary keys detected in table '%s'
What it means
Printed by SetMultiplePrimaryKeyError when V10Builder::AddFieldConstraint receives a PrimaryKey constraint (bit 0b1000, emitted by FIELD_PrimaryKey / FIELD_PrimaryKeyAutoInc macros) for a table whose primary_key list is already non-empty. SpacetimeDB tables allow exactly one primary-key column; the flag poisons module init and __preinit__99_validate_types later swaps the whole module for the error type ERROR_MULTIPLE_PRIMARY_KEYS_<table>, so publishing fails server-side.
Source
Thrown at crates/bindings-cpp/src/internal/Module.cpp:86
};
static std::vector<HttpHandler> g_http_handlers;
// Global error flag for multiple primary key detection
static bool g_multiple_primary_key_error = false;
static std::string g_multiple_primary_key_table_name = "";
static bool g_constraint_registration_error = false;
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,View on GitHub (pinned to 6dee26c6ef)
Solutions
- Keep exactly one primary-key macro (FIELD_PrimaryKey or FIELD_PrimaryKeyAutoInc) per table and delete the duplicate named in the message
- Model composite uniqueness with FIELD_Unique on each column, or a named multi-column index, instead of a second primary key
- If a two-column key is truly needed, merge the columns into one key field (e.g. a struct or concatenated value) and make that the primary key
Example fix
// before FIELD_PrimaryKey(Player, id); FIELD_PrimaryKey(Player, session_id); // second PK -> error // after FIELD_PrimaryKey(Player, id); FIELD_Unique(Player, session_id);
Defensive patterns
Strategy: validation
Validate before calling
# CI guard: every table may appear in at most one primary-key macro
# rg -n 'FIELD_PrimaryKey(_AutoInc|_NAMED)?\s*\(' src/ |
# awk -F'(' '{print $2}' | awk -F',' '{print $1}' | sort | uniq -d
# empty output == OK; any duplicate table name fails the build Prevention
- Keep constraint macros adjacent to their table struct in a single file so duplicates are visible
- Run the duplicate-detection grep in CI before publish
- Map composite SQL keys to FIELD_Unique + multi-column indexes during migration, never to two primary keys
When it happens
Trigger: Two FIELD_PrimaryKey(...) macros for the same table struct; FIELD_PrimaryKey on one column plus FIELD_PrimaryKeyAutoInc on another; macros placed in different translation units - each generates its own __preinit__21_field_constraint_* function, so both run at startup.
Common situations: Migrating SQL schemas that use composite primary keys; copy-pasting a constraint block when adding a new column; renaming FIELD_Unique to FIELD_PrimaryKey on a second field for 'extra safety'.
Related errors
- [CONSTRAINT ERROR] Module cleared and replaced with error t
- Original error: Multiple primary keys detected in table '%s'
- View '${exportName}' can have at most one primaryKey() colum
- When replaying `st_column` update: `table_primary_key` shoul
- ERROR: Constraint registration failed [%s] %s
AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20).
Data as JSON: /api/errors/69c36b79b38590e8.
Report an issue: GitHub.