apache/seatunnel · error · IllegalArgumentException
Unknown Row Op Type
Error message
Unknown Row Op Type: ${row.getOpType()} What it means
Thrown by SeaTunnelRowStreamingRecordDeserializer.deserialize when a TiKV CDC event row carries an op type (RowOpType) that the switch statement does not handle — only PUT, DELETE and UPDATE-after-PUT variants are expected. It signals an unknown/unsupported operation type in the CDC protocol event.
Solutions
- Upgrade connector-cdc-tidb (and its bundled TiKV client proto) to match your TiDB cluster version
- Log and inspect row.getOpType() to identify the unhandled value
- Downgrade/check TiCDC compatibility if you cannot upgrade the connector
- File/verify support for the missing op type in the connector's deserialize switch
Example fix
// before
throw new IllegalArgumentException("Unknown Row Op Type: " + row.getOpType());
// after: upgrade connector version so all TiKV RowOpType values are handled in the switch Defensive patterns
Strategy: type-guard
Validate before calling
// Check op type before deserializing
Object opType = row.getOpType();
if (opType != RowOpType.PUT && opType != RowOpType.DELETE) {
LOG.warn("Skipping unhandled RowOpType {} at ts {}", opType, row.getCommitTs());
} Type guard
static boolean isSupportedOp(Cdcpb.Event.Row row) {
return row.getOpType() == RowOpType.PUT
|| row.getOpType() == RowOpType.DELETE;
} Try / catch
try {
deserializer.deserialize(row, output);
} catch (IllegalArgumentException e) {
if (e.getMessage().startsWith("Unknown Row Op Type")) {
LOG.warn("Skipping unknown op type: {}", row.getOpType());
} else { throw e; }
} Prevention
- Match connector TiKV proto version to your TiDB cluster version
- Log row.getOpType() on unknown values to detect protocol drift early
- Subscribe to TiDB release notes for new CDC op types
When it happens
Trigger: The Cdcpb.Event.Row.getOpType() returns a value outside the handled enum cases, typically due to a TiKV/TiCDC protocol version mismatch or a newly introduced op type the connector does not yet support.
Common situations: Deploying a connector built against an older TiKV client proto against a newer TiDB/TiKV cluster that emits new op types; corrupted or partially parsed CDC event stream.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- Cannot deserialize TiDB DELETE CDC row because both value…
- Commit row has no matched prewrite row yet, hold it and…
- Fail to deserialize row
- Unable to convert TIMESTAMP_TZ from String value
- Unable to convert to decimal from unexpected value
AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10).
Data as JSON: /api/errors/4b6f9d7a23c2fde0.
Report an issue: GitHub.
Appendix: source
Thrown at seatunnel-connectors-v2/connector-cdc/connector-cdc-tidb/src/main/java/org/apache/seatunnel/connectors/seatunnel/cdc/tidb/source/deserializer/SeaTunnelRowStreamingRecordDeserializer.java:84
if (row.getOldValue() == null || row.getOldValue().isEmpty()) {
SeaTunnelRow insert = converter.convert(values, tableInfo, rowType);
insert.setRowKind(RowKind.INSERT);
collect(insert, output);
} else {
SeaTunnelRow update = converter.convert(values, tableInfo, rowType);
update.setRowKind(RowKind.UPDATE_AFTER);
collect(update, output);
}
break;
} catch (final RuntimeException e) {
throw new RuntimeException(
String.format(
"Fail to deserialize row: %s, table: %s",
row, tableInfo.getId()),
e);
}
default:
throw new IllegalArgumentException("Unknown Row Op Type: " + row.getOpType());
}
}
private Object[] decodeDeleteValues(Cdcpb.Event.Row row, long handle) {
ByteString oldValue = row.getOldValue();
if (oldValue != null && !oldValue.isEmpty()) {
return decodeObjects(oldValue.toByteArray(), handle, tableInfo);
}
ByteString value = row.getValue();
if (value != null && !value.isEmpty()) {
// Prefer an available row image before falling back to a PK-only delete row.
return decodeObjects(value.toByteArray(), handle, tableInfo);
}
return decodeDeleteValuesFromHandle(row, handle);
}
private Object[] decodeDeleteValuesFromHandle(Cdcpb.Event.Row row, long handle) {
TiColumnInfo handleColumn = tableInfo.getPKIsHandleColumn();View on GitHub (pinned to cf67b549a7)