{"record":{"id":"69c36b79b38590e8","repo":"clockworklabs/SpacetimeDB","slug":"error-multiple-primary-keys-detected-in-table-s","errorCode":null,"errorMessage":"ERROR: Multiple primary keys detected in table '%s'\n","messagePattern":"ERROR: Multiple primary keys detected in table '(.+?)'\n","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/bindings-cpp/src/internal/Module.cpp","lineNumber":86,"sourceCode":"    };\n    static std::vector<HttpHandler> g_http_handlers;\n    \n    // Global error flag for multiple primary key detection\n    static bool g_multiple_primary_key_error = false;\n    static std::string g_multiple_primary_key_table_name = \"\";\n    static bool g_constraint_registration_error = false;\n    static std::string g_constraint_registration_error_code = \"\";\n    static std::string g_constraint_registration_error_details = \"\";\n    \n    // External global flags for circular reference detection (defined in module_type_registration.cpp)\n    extern bool g_circular_ref_error;\n    extern std::string g_circular_ref_type_name;\n    \n    // Function to set the multiple primary key error flag\n    void SetMultiplePrimaryKeyError(const std::string& table_name) {\n        g_multiple_primary_key_error = true;\n        g_multiple_primary_key_table_name = table_name;\n        fprintf(stderr, \"ERROR: Multiple primary keys detected in table '%s'\\n\", table_name.c_str());\n    }\n\n    void SetConstraintRegistrationError(const std::string& code, const std::string& details) {\n        g_constraint_registration_error = true;\n        g_constraint_registration_error_code = code;\n        g_constraint_registration_error_details = details;\n        fprintf(stderr, \"ERROR: Constraint registration failed [%s] %s\\n\", code.c_str(), details.c_str());\n    }\n    \n    // Register a reducer handler (called by V9Builder during registration)\n    void RegisterReducerHandler(const std::string& name, \n                                std::function<void(ReducerContext&, BytesSource)> handler,\n                                std::optional<Lifecycle> lifecycle) {\n        g_reducer_handlers.push_back({name, handler, lifecycle});\n    }\n    \n    // Register a view handler (called by V9Builder during registration)\n    void RegisterViewHandler(const std::string& name,","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/6dee26c6efc2856793e12b148a59742964f5d783/crates/bindings-cpp/src/internal/Module.cpp#L68-L104","documentation":"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.","triggerScenarios":"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.","commonSituations":"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'.","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"],"exampleFix":"// before\nFIELD_PrimaryKey(Player, id);\nFIELD_PrimaryKey(Player, session_id); // second PK -> error\n\n// after\nFIELD_PrimaryKey(Player, id);\nFIELD_Unique(Player, session_id);","handlingStrategy":"validation","validationCode":"# CI guard: every table may appear in at most one primary-key macro\n# rg -n 'FIELD_PrimaryKey(_AutoInc|_NAMED)?\\s*\\(' src/ |\n#   awk -F'(' '{print $2}' | awk -F',' '{print $1}' | sort | uniq -d\n# empty output == OK; any duplicate table name fails the build","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["spacetimedb","cpp","table","schema","primary-key","constraint"],"backgroundTag":"multiple-primary-keys","analyzedSha":"6dee26c6efc2856793e12b148a59742964f5d783","analyzedAt":"2026-08-20T06:08:37.179Z","contentChangedAt":"2026-08-20T06:08:37.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}