apache/seatunnel · error · SeaTunnelEngineException
Unsupported row type
Error message
Unsupported row type: ${rowType.getClass().getName()} What it means
SeaTunnelSourceCollector.collect computes a row's byte size using its row type. It supports SeaTunnelRowType (single table) and MultipleRowType (multi-table via a per-table type map); any other SeaTunnelRowType implementation reaches the else branch and throws SeaTunnelEngineException with the class name. This guards the flow-control/metrics path against unknown row-type wrappers.
Solutions
- Upgrade/align the connector jar and engine versions so both use the same SeaTunnelRowType classes
- Make the source emit standard SeaTunnelRowType (or MultipleRowType for multi-table sources)
- Add a branch in SeaTunnelSourceCollector for the new row type if it is legitimately supported
- Check the reported class name to identify which connector/version produced the unsupported type
Example fix
// before (custom source)
return new MyCustomRowType(...); // unsupported by collector
// after
return new SeaTunnelRowType(new String[]{"id"}, new SeaTunnelDataType[]{BasicType.LONG_TYPE}); Defensive patterns
Strategy: try-catch
Validate before calling
if (!(rowType instanceof SeaTunnelRowType) && !(rowType instanceof MultipleRowType)) throw new IllegalArgumentException("Unsupported row type: " + rowType.getClass().getName()); Type guard
boolean isSupportedRowType(SeaTunnelRowType t) { return t instanceof SeaTunnelRowType || t instanceof MultipleRowType; } Try / catch
try { collector.collect(row); } catch (SeaTunnelEngineException e) { log.error("Row type {} unsupported by engine; align connector jar with engine version", e.getMessage()); failTask(e); } Prevention
- Emit only SeaTunnelRowType or MultipleRowType from sources
- Shade/pin seatunnel-api consistently between connectors and engine
- Test custom sources end-to-end on Zeta before release
When it happens
Trigger: A source emits rows whose RowType container is neither SeaTunnelRowType nor MultipleRowType — typically a custom catalog table type or a newer/older SeaTunnelRowType variant crossing engine versions.
Common situations: Custom source connectors returning bespoke row types; mixed seatunnel-api versions between a connector fat jar and the Zeta engine; schema-change scenarios (multi-table) where the collector's type map is populated only for MultipleRowType.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Airtable API rate limit reached, retry
- At least one source plugin must be configured.
- BigtableSourceSplitEnumerator already closed; cannot create…
- BigtableSourceSplitEnumerator closed during client creation
- Checkpoint storage is unavailable
AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10).
Data as JSON: /api/errors/8c49a2d825ad4d1c.
Report an issue: GitHub.
Appendix: source
Thrown at seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/task/SeaTunnelSourceCollector.java:304
/** Updates source-side metrics, samples new traces when enabled, and forwards the record. */
@Override
public void collect(T row) {
if (dryRunSampleEnabled && dryRunSampleCount >= dryRunSampleLimit) {
return;
}
try {
if (row instanceof SeaTunnelRow) {
String tableId = ((SeaTunnelRow) row).getTableId();
// init the size of row early with rowType, this way is faster than init the size
// without rowType
int size;
if (rowType instanceof SeaTunnelRowType) {
size = ((SeaTunnelRow) row).getBytesSize((SeaTunnelRowType) rowType);
} else if (rowType instanceof MultipleRowType) {
size = ((SeaTunnelRow) row).getBytesSize(rowTypeMap.get(tableId));
} else {
throw new SeaTunnelEngineException(
"Unsupported row type: " + rowType.getClass().getName());
}
flowControlGate.audit((SeaTunnelRow) row);
connectorMetricsCalcContext.updateMetrics(row, tableId);
tryStainTrace((SeaTunnelRow) row);
}
if (dryRunSamplePrintData) {
dryRunSampleCount++;
log.info("Dry-run sample [source] row {}: {}", dryRunSampleCount, row);
} else if (dryRunSampleEnabled) {
dryRunSampleCount++;
}
sendRecordToNext(new Record<>(row));
emptyThisPollNext = false;
if (dryRunSampleEnabled) {
if (dryRunSampleCount == dryRunSampleLimit && dryRunSampleComplete != null) {
dryRunSampleComplete.run();
}View on GitHub (pinned to cf67b549a7)