apache/seatunnel · error · UnsupportedOperationException
Unknown row kind: ${rowKind}
Error message
Unknown row kind: ${rowKind} What it means
changeFlag() maps a SeaTunnel RowKind to a boolean (insert/update-after -> true, delete/update-before -> false); any other RowKind (e.g. EMPTY) hits default and throws UnsupportedOperationException because the sink cannot interpret it as a change flag.
Source
Thrown at seatunnel-connectors-v2/connector-hudi/src/main/java/org/apache/seatunnel/connectors/seatunnel/hudi/sink/writer/HudiRecordWriter.java:212
protected void prepareRecords(SeaTunnelRow element) {
HoodieRecord<HoodieAvroPayload> hoodieAvroPayloadHoodieRecord =
recordConverter.convertRow(schema, seaTunnelRowType, element, hudiTableConfig);
HoodieKey recordKey = hoodieAvroPayloadHoodieRecord.getKey();
boolean changeFlag = changeFlag(element.getRowKind());
buffer.put(recordKey, Pair.of(changeFlag, hoodieAvroPayloadHoodieRecord));
}
private boolean changeFlag(RowKind rowKind) {
switch (rowKind) {
case DELETE:
case UPDATE_BEFORE:
return false;
case INSERT:
case UPDATE_AFTER:
return true;
default:
throw new UnsupportedOperationException("Unknown row kind: " + rowKind);
}
}
protected void checkFlushException() {
if (flushException != null) {
throw new HudiConnectorException(
HudiErrorCode.FLUSH_DATA_FAILED,
"Flush records to Hudi failed.",
flushException);
}
}
/** Executes prepared statement and closes all resources of this instance. */
public synchronized void close() {
if (!closed) {
closed = true;
try {
flush();View on GitHub (pinned to cf67b549a7)
Solutions
- Ensure the source/transform sets an explicit RowKind (INSERT for append-only data)
- If appending plain data, use a source or transform that sets RowKind.INSERT
- Verify translation/adapter settings so CDC events get proper row kinds
Example fix
// before: row created with RowKind.ENUM_EMPTY // after row.setRowKind(RowKind.INSERT);
Defensive patterns
Strategy: type-guard
Validate before calling
RowKind kind = row.getRowKind();
if (!(kind == RowKind.INSERT || kind == RowKind.UPDATE_AFTER
|| kind == RowKind.UPDATE_BEFORE || kind == RowKind.DELETE)) {
throw new IllegalStateException("RowKind " + kind + " not usable by Hudi CDC sink");
} Type guard
static boolean hasUsableRowKind(SeaTunnelRow row) {
RowKind k = row.getRowKind();
return k == RowKind.INSERT || k == RowKind.UPDATE_AFTER
|| k == RowKind.UPDATE_BEFORE || k == RowKind.DELETE;
} Try / catch
try {
sink.writeRecord(row);
} catch (UnsupportedOperationException e) {
if (e.getMessage().startsWith("Unknown row kind:")) {
LOG.error("Set an explicit RowKind on rows before the Hudi sink");
} else { throw e; }
} Prevention
- Use CDC-capable sources or set RowKind.INSERT for append-only data
- Audit custom transforms to ensure they preserve/set RowKind
- Add a pre-submit validation on sample rows' row kinds
When it happens
Trigger: writeRecord calls changeFlag(rowKind) on a row whose RowKind is not INSERT, UPDATE_AFTER, UPDATE_BEFORE, or DELETE — typically RowKind.ENUM_EMPTY or a custom kind.
Common situations: Non-CDC source feeding a CDC-style Hudi sink, producing EMPTY row kind; custom transforms not setting RowKind; translation layers emitting unusual kinds.
Related errors
- Unknown row kind:
- Multiple incremental splits are not supported
- not supported create new Offset by committed offset.
- The SplitChange type of %s is not supported.
- should not call here, error
AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10).
Data as JSON: /api/errors/8c77c713c5958468.
Report an issue: GitHub.