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
- Configure recordKeyFields to point at non-nullable columns that are populated in the source data
- Add an upstream transform (e.g. SQL/Filter) to drop or repair rows with null key fields before the Hudi sink
- Set a Hudi precombine/key strategy or generate a default key (e.g. UUID) upstream when source rows lack a natural key
- 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
- Design recordKeyFields to reference NOT NULL primary-key columns
- Validate a sample of source rows for null keys before job submission
- Add a Filter transform to drop null-key rows explicitly
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
- DATA_TYPE_CAST_FAILED
- Map key cannot be null
- Close Hudi record writer failed.
- MultiTableWriterRunnable can't find writer for tableId:
- The Factory has not been implemented and the deprecated Plug
AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10).
Data as JSON: /api/errors/90b6d787af5c6b0e.
Report an issue: GitHub.