clockworklabs/SpacetimeDB · error

ERROR: Field %s.%s has primary_key constraint - cannot have

Error message

ERROR: Field %s.%s has primary_key constraint - cannot have default value

What it means

AddColumnDefault enforces the schema rule that a column with a primary_key constraint cannot also have a default value: if the resolved field index appears in table->primary_key, it prints this message (note: without a trailing newline) and drops the default. Primary key values are identity-assigned by the database, so a client-supplied default would conflict with key generation.

Source

Thrown at crates/bindings-cpp/include/spacetimedb/internal/v9_builder.h:645

            field_found = true;
            break;
        }
        field_idx++;
    }
    
    if (!field_found) {
        fprintf(stderr, "ERROR: Field '%s' not found in table '%s'\n",
                field_name.c_str(), table_name.c_str());
        return;
    }
    
    // Validate: default values cannot be used with primary_key, unique, or auto_inc
    // Check if this column is in the primary key
    for (uint16_t pk_col : table->primary_key) {
        if (pk_col == field_idx) {
            std::string error_msg = "ERROR: Field " + table_name + "." + field_name + 
                        " has primary_key constraint - cannot have default value";
            fprintf(stderr, "%s", error_msg.c_str());
            // fprintf(stderr, "ERROR:  has primary_key constraint - cannot have default value",
            //          table_name.c_str(), field_name.c_str());
            return;
        }
    }
    
    // Check if this column has a unique constraint
    for (const auto& constraint : table->constraints) {
        if (constraint.data.get_tag() == 0) {  // Unique constraint variant
            const auto& unique_data = constraint.data.get<0>();
            if (unique_data.columns.size() == 1 && unique_data.columns[0] == field_idx) {
                fprintf(stderr, "ERROR: Field %s.%s has unique constraint - cannot have default value",
                        table_name.c_str(), field_name.c_str());
                return;
            }
        }
    }
    

View on GitHub (pinned to 524b4487d9)

Solutions

  1. Remove the column default from the primary key column — keys are generated, not defaulted
  2. If a specific key value is required, generate it in the init reducer instead of a column default
  3. Rebuild and confirm no 'primary_key constraint - cannot have default value' message remains
  4. Audit for the sibling checks (unique, auto_inc) on the same column

Example fix

// before
ST_TABLE(user, ...);   // id is primary key
ST_COLUMN_DEFAULT(user, id, 0);   // conflict: pk + default
// after
ST_TABLE(user, ...);   // id is primary key, value assigned by the db
// (default removed)
Defensive patterns

Strategy: validation

Validate before calling

// Policy check before adding a default: refuse defaults on pk columns
// (after init, with table resolved and field_idx computed):
for (uint16_t pk : table.primary_key)
    if (pk == field_idx) /* skip/forbid the ST_COLUMN_DEFAULT annotation */;

Prevention

When it happens

Trigger: Marking a column both ST_PRIMARY_KEY/#[primary_key] and ST_COLUMN_DEFAULT on the same table; the primary key macro ran first (so table->primary_key is populated) and then AddColumnDefault resolves the same field_idx in that list.

Common situations: Copying a default onto an id column that was later promoted to primary key; schema migrations that add primary_key without removing existing defaults; mismatches between macro ordering making the conflict intermittent between builds.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16). Data as JSON: /api/errors/5b720d5434c79745. Report an issue: GitHub.