apache/seatunnel · error · HoodieKeyException

recordKey values: "${recordKey}" for fields: ${recordKeyFiel

Error message

recordKey values: "${recordKey}" for fields: ${recordKeyFields} cannot be entirely null or empty.

What it means

HudiRecordConverter.getRecordKey builds the Hudi record key by concatenating the values of the configured record key fields; if every field value is null or empty, it throws HoodieKeyException because a Hudi record must have a non-empty key for deduplication and indexing.

Source

Thrown at seatunnel-connectors-v2/connector-hudi/src/main/java/org/apache/seatunnel/connectors/seatunnel/hudi/sink/convert/HudiRecordConverter.java:116

                recordKey
                        .append(recordKeyField)
                        .append(":")
                        .append(NULL_RECORD_KEY_PLACEHOLDER)
                        .append(",");
            } else if (recordKeyValue.isEmpty()) {
                recordKey
                        .append(recordKeyField)
                        .append(":")
                        .append(EMPTY_RECORD_KEY_PLACEHOLDER)
                        .append(",");
            } else {
                recordKey.append(recordKeyField).append(":").append(recordKeyValue).append(",");
                keyIsNullEmpty = false;
            }
        }
        recordKey.deleteCharAt(recordKey.length() - 1);
        if (keyIsNullEmpty) {
            throw new HoodieKeyException(
                    "recordKey values: \""
                            + recordKey
                            + "\" for fields: "
                            + hudiTableConfig.getRecordKeyFields()
                            + " cannot be entirely null or empty.");
        }
        return recordKey.toString();
    }

    public String getRecordPartitionPath(
            SeaTunnelRow element,
            SeaTunnelRowType seaTunnelRowType,
            HudiTableConfig hudiTableConfig) {
        if (hudiTableConfig.getPartitionFields().isEmpty()) {
            return "";
        }

        StringBuilder partitionPath = new StringBuilder();

View on GitHub (pinned to cf67b549a7)

Solutions

  1. Configure recordKeyFields to point at non-nullable columns that are populated in the source data
  2. Add an upstream transform (e.g. SQL/Filter) to drop or repair rows with null key fields before the Hudi sink
  3. Set a Hudi precombine/key strategy or generate a default key (e.g. UUID) upstream when source rows lack a natural key
  4. Verify field names in recordKeyFields match the actual SeaTunnel schema (typos yield null lookups)

Example fix

// before: recordKeyFields = "user_id" but rows have null user_id
// after: coalesce upstream or use a generated key
SQL transform: SELECT COALESCE(user_id, uuid()) AS user_id, ... FROM source
Defensive patterns

Strategy: validation

Validate before calling

boolean allKeyFieldsNull = recordKeyFields.stream().allMatch(f -> row.getField(rowType.indexOf(f)) == null || "".equals(row.getField(rowType.indexOf(f))));
if (allKeyFieldsNull) { throw new IllegalArgumentException("All record key fields are null; fix data or key config before writing to Hudi"); }

Type guard

static boolean hasNonNullKey(SeaTunnelRow row, SeaTunnelRowType type, List<String> keyFields) {
    return keyFields.stream().anyMatch(f -> {
        int i = type.indexOf(f);
        return i >= 0 && row.getField(i) != null && !String.valueOf(row.getField(i)).isEmpty();
    });
}

Prevention

When it happens

Trigger: Calling getRecordKey (via rowKey) on a SeaTunnelRow where all fields listed in the Hudi table's recordKeyFields configuration resolve to null or empty strings.

Common situations: Source data with null key columns (e.g. CDC rows with missing primary key); misconfigured recordKeyFields pointing at optional/nullable columns; upstream transforms dropping the key field.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10). Data as JSON: /api/errors/90b6d787af5c6b0e. Report an issue: GitHub.