risingwavelabs/risingwave · error

MySQL table doesn't define the primary key

Error message

MySQL table doesn't define the primary key

What it means

When discovering the schema of a CDC source table, RisingWave requires a primary key to construct its internal column descriptors (pk_names). MySQL tables without a PRIMARY KEY (or usable unique index) cannot be tracked for CDC, so connect() aborts schema discovery with this error.

Source

Thrown at src/connector/src/source/cdc/external/mysql.rs:193

                        );
                        None
                    });

                ColumnDesc::named_with_default_value(
                    col_name.clone(),
                    ColumnId::placeholder(),
                    data_type.clone(),
                    snapshot_value,
                )
            } else {
                ColumnDesc::named(col_name.clone(), ColumnId::placeholder(), data_type)
            };

            column_descs.push(column_desc);
        }

        let pk_names = primary_key_names(&indexes)
            .ok_or_else(|| anyhow!("MySQL table doesn't define the primary key"))?;
        Ok(Self {
            column_descs,
            pk_names,
        })
    }

    pub fn column_descs(&self) -> &Vec<ColumnDesc> {
        &self.column_descs
    }

    pub fn pk_names(&self) -> &Vec<String> {
        &self.pk_names
    }
}

fn primary_key_names(indexes: &[IndexInfo]) -> Option<Vec<String>> {
    indexes
        .iter()

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Add a PRIMARY KEY to the MySQL source table (ALTER TABLE ... ADD PRIMARY KEY (...)).
  2. Create the RisingWave table on a different table that has a primary key.
  3. If no key can be added, CDC replication of that table is unsupported; pick another ingestion method.

Example fix

// before
CREATE TABLE orders (...); -- no PK in MySQL
// after
ALTER TABLE orders ADD PRIMARY KEY (id);
Defensive patterns

Strategy: validation

Validate before calling

-- run on MySQL before creating the CDC table
SELECT TABLE_NAME FROM information_schema.TABLES t
WHERE t.TABLE_SCHEMA = 'mydb' AND t.TABLE_NAME = 'orders'
AND EXISTS (SELECT 1 FROM information_schema.TABLE_CONSTRAINTS c
  WHERE c.TABLE_SCHEMA = t.TABLE_SCHEMA AND c.TABLE_NAME = t.TABLE_NAME
  AND c.CONSTRAINT_TYPE = 'PRIMARY KEY');

Type guard

async function tableHasPrimaryKey(conn, schema, table) {
  const rows = await conn.query(
    "SELECT 1 FROM information_schema.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA=? AND TABLE_NAME=? AND CONSTRAINT_TYPE='PRIMARY KEY'", [schema, table]);
  return rows.length > 0;
}

Try / catch

try { await createCdcTable('orders'); } catch (e) { if (String(e).includes("doesn't define the primary key")) { await mysqlExec('ALTER TABLE orders ADD PRIMARY KEY (id)'); } else throw e; }

Prevention

When it happens

Trigger: CREATE TABLE ... WITH (connector='mysql-cdc') referencing a MySQL table that has no PRIMARY KEY defined.

Common situations: Legacy tables created without keys; views or tables relying on unique secondary indexes only; developer assumed a unique index is enough.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/6fead95d63757440. Report an issue: GitHub.