apache/seatunnel · error · IotdbConnectorException

ILLEGAL_ARGUMENT

ILLEGAL_ARGUMENT

Error message

Illegal SeaTunnelRowType: 

What it means

DefaultSeaTunnelRowDeserializer.convert() for table-format records expects the IoTDB RowRecord field count to equal rowType.getTotalFields() - 1 (the timestamp becomes the first SeaTunnel column). A mismatch throws ILLEGAL_ARGUMENT with the offending record.

Source

Thrown at seatunnel-connectors-v2/connector-iotdb-v2/src/main/java/org/apache/seatunnel/connectors/seatunnel/iotdbv2/serialize/DefaultSeaTunnelRowDeserializer.java:57

public class DefaultSeaTunnelRowDeserializer implements SeaTunnelRowDeserializer {

    private final SeaTunnelRowType rowType;

    private final String sqlDialect;

    @Override
    public SeaTunnelRow deserialize(RowRecord rowRecord) {
        if (SourceConstants.TABLE.equalsIgnoreCase(sqlDialect)) {
            return convertTableRow(rowRecord);
        }
        return convert(rowRecord);
    }

    private SeaTunnelRow convert(RowRecord rowRecord) {
        long timestamp = rowRecord.getTimestamp();
        List<Field> fields = rowRecord.getFields();
        if (fields.size() != (rowType.getTotalFields() - 1)) {
            throw new IotdbConnectorException(
                    CommonErrorCode.ILLEGAL_ARGUMENT, "Illegal SeaTunnelRowType: " + rowRecord);
        }
        Object[] seaTunnelFields = new Object[rowType.getTotalFields()];
        seaTunnelFields[0] = convertTimestamp(timestamp, rowType.getFieldType(0));
        for (int i = 1; i < rowType.getTotalFields(); i++) {
            Field field = fields.get(i - 1);
            if (field == null || field.getDataType() == null) {
                seaTunnelFields[i] = null;
                continue;
            }
            SeaTunnelDataType<?> seaTunnelFieldType = rowType.getFieldType(i);
            seaTunnelFields[i] = convert(seaTunnelFieldType, field);
        }
        return new SeaTunnelRow(seaTunnelFields);
    }

    private SeaTunnelRow convertTableRow(RowRecord rowRecord) {
        List<Field> fields = rowRecord.getFields();

View on GitHub (pinned to cf67b549a7)

Solutions

  1. Align the SeaTunnel catalog schema so totalFields == IoTDB fields + 1 (timestamp first).
  2. Re-check the IoTDB query's selected measurements; ensure all are present in each record.
  3. Update the job config after any IoTDB timeseries schema change.

Example fix

// before
rowType fields = [ts, temp]  // IoTDB returns 2 fields
// after
rowType fields = [ts, temp, humidity] // matches IoTDB's 2 fields + timestamp
Defensive patterns

Strategy: validation

Validate before calling

// before deserialization
if (iotdbRecord.getFields().size() + 1 != rowType.getTotalFields()) {
    throw new IllegalStateException("Schema mismatch: IoTDB fields=" + iotdbRecord.getFields().size()
        + " expected=" + (rowType.getTotalFields() - 1));
}

Try / catch

try {
    SeaTunnelRow row = deserializer.deserialize(record);
} catch (IotdbConnectorException e) {
    log.error("Record does not match declared schema: {}", e.getMessage());
    throw e;
}

Prevention

When it happens

Trigger: Deserializing an IoTDB RowRecord whose field count differs from the SeaTunnelRowType total minus one, e.g., the IoTDB measurement set changed or the declared schema has wrong column count.

Common situations: Measurements added/removed in IoTDB after the SeaTunnel job was configured, schema mismatch between source table definition and IoTDB timeseries, or selected measurements filtered out by the query.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10). Data as JSON: /api/errors/cbd7051897a05bb9. Report an issue: GitHub.