apache/seatunnel · error · IllegalStateException
Unknown operation type '${op}'.
Error message
Unknown operation type '${op}'. What it means
Debezium change events carry an 'op' field ('c' create, 'u' update, 'd' delete, 'r' read/snapshot). parsePayload switches on this value and throws IllegalStateException for anything else, since SeaTunnel cannot map an unknown operation to a RowKind. This guards against malformed, corrupted, or forward-incompatible Debezium messages.
Source
Thrown at seatunnel-formats/seatunnel-format-json/src/main/java/org/apache/seatunnel/format/json/debezium/DebeziumJsonDeserializationSchema.java:179
out.collect(after);
break;
case OP_DELETE:
SeaTunnelRow delete = debeziumRowConverter.parse(payload.get(DATA_BEFORE));
if (delete == null) {
throw new IllegalStateException(
String.format(REPLICA_IDENTITY_EXCEPTION, "DELETE"));
}
delete.setRowKind(RowKind.DELETE);
if (tablePath != null) {
delete.setTableId(tablePath.toString());
}
if (tsNode != null) {
MetadataUtil.setEventTime(delete, tsNode.asLong());
}
out.collect(delete);
break;
default:
throw new IllegalStateException(format("Unknown operation type '%s'.", op));
}
}
@Override
public SeaTunnelDataType<SeaTunnelRow> getProducedType() {
return this.rowType;
}
private JsonNode getPayload(JsonNode jsonNode) {
if (debeziumEnabledSchema) {
return jsonNode.get(DATA_PAYLOAD);
}
return jsonNode;
}
}
View on GitHub (pinned to cf67b549a7)
Solutions
- Inspect the offending message's 'op' value and confirm it is one of c/u/d/r
- Filter unsupported event types (truncate/message) using Debezium SMTs or topic routing
- Enable the format's ignoreParseErrors option if skipping unparseable messages is acceptable
- Align SeaTunnel and Debezium versions so the set of supported ops matches
Example fix
// before: raw topic includes truncate events (op='t') -> parse fails // after: drop them upstream with a Debezium SMT / RegexRouter "transforms": "route", "transforms.route.type": "org.apache.kafka.connect.transforms.RegexRouter", "transforms.route.regex": "cdc_(.*)", "transforms.route.replacement": "$1"
Defensive patterns
Strategy: try-catch
Validate before calling
JsonNode op = payload.get("op");
if (op == null || !"cudr".contains(op.asText(""))) {
// skip or route to DLQ before parsing
} Try / catch
try {
deserializer.deserialize(message, out);
} catch (IllegalStateException e) {
if (e.getMessage().startsWith("Unknown operation type")) {
log.warn("Skipping unsupported Debezium op: {}", e.getMessage());
return; // or send to DLQ
}
throw e;
} Prevention
- Filter non-data events (truncate/message) with SMTs or topic routing
- Enable ignoreParseErrors for tolerant pipelines
- Keep Debezium and SeaTunnel format versions aligned
When it happens
Trigger: A message JSON reaching parsePayload whose op field is missing, null, empty, or an unexpected value (e.g. 't' truncate, 'm' message, or a corrupted character).
Common situations: Non-data change events (truncate, heartbeat/message events) flowing through the CDC topic; Debezium version drift producing new op values; hand-crafted or test payloads missing 'op'; Kafka topic routing delivering non-change-event envelopes.
Related errors
- Unable to instantiate the database history class " + config.
- Data change record shouldn't use READ operation, the the rec
- The table changes should only have one element
- Unknown table change type:
- should not call here, error
AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10).
Data as JSON: /api/errors/31b096d15019417d.
Report an issue: GitHub.