apache/seatunnel · error · SeaTunnelException

Incremental snapshot for tables requires primary key, but ta

Error message

Incremental snapshot for tables requires primary key, but table ${table.id()} doesn't have primary key.

What it means

PostgresUtils.getSplitType converts the table's primary key columns into the split key type used by the incremental snapshot framework. Incremental snapshots chop tables into chunks by a key column, so if table.primaryKeyColumns() is empty it throws SeaTunnelException stating the table must have a primary key.

Source

Thrown at seatunnel-connectors-v2/connector-cdc/connector-cdc-postgres/src/main/java/org/apache/seatunnel/connectors/seatunnel/cdc/postgres/utils/PostgresUtils.java:265

                        chunkSize);
        return jdbc.prepareQueryAndMap(
                query,
                ps -> ps.setObject(1, includedLowerBound),
                rs -> {
                    if (!rs.next()) {
                        // this should never happen
                        throw new SQLException(
                                String.format(
                                        "No result returned after running query [%s]", query));
                    }
                    return rs.getObject(1);
                });
    }

    public static SeaTunnelRowType getSplitType(Table table) {
        List<Column> primaryKeys = table.primaryKeyColumns();
        if (primaryKeys.isEmpty()) {
            throw new SeaTunnelException(
                    String.format(
                            "Incremental snapshot for tables requires primary key,"
                                    + " but table %s doesn't have primary key.",
                            table.id()));
        }

        // use first field in primary key as the split key
        return getSplitType(primaryKeys.get(0));
    }

    public static SeaTunnelRowType getSplitType(Column splitColumn) {
        return new SeaTunnelRowType(
                new String[] {splitColumn.name()},
                new SeaTunnelDataType<?>[] {PostgresTypeUtils.convertFromColumn(splitColumn)});
    }

    public static Offset getLsnPosition(SourceRecord record) {
        return getLsnPosition(record.sourceOffset());

View on GitHub (pinned to cf67b549a7)

Solutions

  1. Add a primary key to the table: ALTER TABLE <t> ADD PRIMARY KEY (<cols>)
  2. Choose an existing NOT NULL UNIQUE column as the effective key if a PK cannot be added
  3. Disable incremental snapshot (scan.incremental.snapshot.enabled=false) to fall back to the lock-based snapshot mode, if supported for your version
  4. Verify the connector's schema read can see the constraint (privileges/catalog visibility)

Example fix

// before
CREATE TABLE users (id bigint, name text);
// after
ALTER TABLE users ADD PRIMARY KEY (id);
Defensive patterns

Strategy: validation

Validate before calling

SELECT a.attname FROM pg_index i
JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey)
WHERE i.indrelid = 'public.orders'::regclass AND i.indisprimary;

Prevention

When it happens

Trigger: Enabling incremental snapshot (scan.incremental.snapshot.enabled=true, the default) on a Postgres table defined without a PRIMARY KEY constraint, or where the PK is not visible to the schema reader (e.g. PK excluded from publication/schema read).

Common situations: Legacy Postgres tables created without PKs; tables whose PK was dropped for bulk-load performance; partitioned tables where the PK lives on partitions; readers with insufficient privilege to see constraints.

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 apache/seatunnel@cf67b549a7 (2026-09-10). Data as JSON: /api/errors/29ddace1cda95df9. Report an issue: GitHub.