apache/seatunnel · warning

Received operation, skip

Error message

Received {} operation, skip

What it means

A Debezium change record carried an operation type the data-record path does not emit (the handled branches cover insert/read/update-after). deserializeDataChangeRecord logs this warning with the operation name and returns without collecting a row, so the record produces no downstream output.

Solutions

  1. Identify the operation from the warning; TRUNCATE and heartbeat skips are usually benign.
  2. If DELETE rows are being skipped unexpectedly, enable/fix the delete-handling mode (e.g. support delete events / tombstone handling) in the deserializer configuration.
  3. Avoid TRUNCATE on monitored tables or implement truncate propagation in a custom deserializer.
  4. Filter heartbeat/tombstone records out of the source table list so they never reach the data path.
  5. Compare source vs sink row counts to confirm no real data loss is hidden behind these skips.

Example fix

// before
} else {
    log.warn("Received {} operation, skip", operation);
}
// after: explicitly handle truncate, log others at debug
} else if (operation == Operation.TRUNCATE) {
    log.info("Received TRUNCATE for {}, not propagated", record.topic());
} else {
    log.warn("Received {} operation, skip", operation);
}
Defensive patterns

Strategy: fallback

Validate before calling

// pre-filter non-emittable operations so they never reach the data path
String opName = record.valueSchema().name();
if (opName.endsWith("TransactionMetadata") || opName.endsWith("Heartbeat")) {
    return;
}

Type guard

boolean isEmittable(Operation op) {
    return op == Operation.CREATE || op == Operation.READ || op == Operation.UPDATE;
}

Try / catch

// this path does not throw; guard on the emitted row instead
if (row == null) {
    log.debug("Record skipped due to operation {}", operation);
    return;
}
collector.collect(row);

Prevention

When it happens

Trigger: deserializeDataChangeRecord receives an Operation outside the handled insert/read/update branches — typically TRUNCATE, or a DELETE whose handling path is not enabled by the configured delete-handling mode, or a heartbeat/message record routed to the data path.

Common situations: Running TRUNCATE TABLE on a CDC-monitored table; Debezium heartbeat events reaching the deserializer; connectors where delete handling is disabled and DELETE ops fall into the skip branch; replication events the deserializer version does not recognize.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at seatunnel-connectors-v2/connector-cdc/connector-cdc-base/src/main/java/org/apache/seatunnel/connectors/cdc/debezium/row/SeaTunnelRowDebeziumDeserializeSchema.java:312

            MetadataUtil.setBinlogFile(before, binlogFile);
            MetadataUtil.setBinlogPos(before, binlogPos);
            MetadataUtil.setBinlogRow(before, binlogRow);
            MetadataUtil.setGtid(before, gtid);
            collector.collect(before);

            SeaTunnelRow after = extractAfterRow(converters, record, messageStruct, valueSchema);
            after.setRowKind(RowKind.UPDATE_AFTER);
            after.setTableId(tableId);
            MetadataUtil.setDelay(after, delay);
            MetadataUtil.setEventTime(after, fetchTimestamp);
            MetadataUtil.setSourceTimestamp(after, messageTimestamp);
            MetadataUtil.setBinlogFile(after, binlogFile);
            MetadataUtil.setBinlogPos(after, binlogPos);
            MetadataUtil.setBinlogRow(after, binlogRow);
            MetadataUtil.setGtid(after, gtid);
            collector.collect(after);
        } else {
            log.warn("Received {} operation, skip", operation);
        }
    }

    private SeaTunnelRow extractAfterRow(
            SeaTunnelRowDebeziumDeserializationConverters runtimeConverter,
            SourceRecord record,
            Struct value,
            Schema valueSchema)
            throws Exception {

        Schema afterSchema = valueSchema.field(Envelope.FieldName.AFTER).schema();
        Struct after = value.getStruct(Envelope.FieldName.AFTER);
        return runtimeConverter.convert(record, after, afterSchema);
    }

    private SeaTunnelRow extractBeforeRow(
            SeaTunnelRowDebeziumDeserializationConverters runtimeConverter,
            SourceRecord record,

View on GitHub (pinned to cf67b549a7)