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

  1. Keep exactly one primary-key macro (FIELD_PrimaryKey or FIELD_PrimaryKeyAutoInc) per table and delete the duplicate named in the message
  2. Model composite uniqueness with FIELD_Unique on each column, or a named multi-column index, instead of a second primary key
  3. 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

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


AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20). Data as JSON: /api/errors/69c36b79b38590e8. Report an issue: GitHub.