apache/seatunnel · error · UnsupportedOperationException

The SplitChange type of %s is not supported.

Error message

The SplitChange type of %s is not supported.

What it means

FlussSourceSplitReader only accepts SplitsAddition changes; any other SplitsChange subtype (e.g. SplitsRemoval) is rejected with this UnsupportedOperationException. The reader cannot retract or remove already-subscribed splits.

Source

Thrown at seatunnel-connectors-v2/connector-fluss/src/main/java/org/apache/seatunnel/connectors/seatunnel/fluss/source/FlussSourceSplitReader.java:211

     * reports the split in {@code finishedSplits}, yet the base only drops its state one {@code
     * pollNext} later (in {@code finishCurrentFetch}), after the split's last record has already
     * advanced {@code currentOffset} to the end. A checkpoint landing in that window persists a
     * just-completed split, and restoring it hands us back {@code start == end}. Finishing it here
     * skips a pointless subscribe + poll and avoids a misleading out-of-range warning should
     * retention have meanwhile passed that end.
     *
     * <p>The {@link LogScanner#EARLIEST_OFFSET} (-2) sentinel of a fresh split floors to 0, so a
     * fresh split is only drained when its end offset is 0 — an empty bucket, which the enumerator
     * already filters out, leaving that a defensive fallback.
     */
    static boolean isDrainedAtAssignment(long startOffset, long endOffset) {
        return isBounded(endOffset) && Math.max(startOffset, 0L) >= endOffset;
    }

    @Override
    public void handleSplitsChanges(SplitsChange<FlussSourceSplit> splitsChanges) {
        if (!(splitsChanges instanceof SplitsAddition)) {
            throw new UnsupportedOperationException(
                    String.format(
                            "The SplitChange type of %s is not supported.",
                            splitsChanges.getClass()));
        }
        for (FlussSourceSplit split : splitsChanges.splits()) {
            long startOffset = split.getStartOffset();
            long endOffset = split.getEndOffset();
            if (isDrainedAtAssignment(startOffset, endOffset)) {
                drainedSplits.add(split.splitId());
                log.info(
                        "Split {} has nothing to read (startOffset={}, endOffset={}); marking finished without subscribing",
                        split.splitId(),
                        startOffset,
                        endOffset);
                continue;
            }
            if (tableScan == null) {
                tableScan = createTableScan();

View on GitHub (pinned to cf67b549a7)

Solutions

  1. Ensure only SplitsAddition is sent to this reader (enumerator should not reassign splits of live readers)
  2. In restore/rebalance paths, recreate the reader instead of sending SplitsRemoval
  3. Patch handleSplitsChanges to no-op or handle SplitsRemoval if the runtime legitimately sends it
  4. Upgrade connector/runtime versions so split redistribution matches the push-only model

Example fix

// before (enumerator)
enumeratorContext.assignSplit(new SplitsRemoval<>(splits, subtaskId));
// after
// do not send removals; splits stay with their reader until task teardown
Defensive patterns

Strategy: type-guard

Validate before calling

if (!(change instanceof SplitsAddition)) {
  throw new IllegalArgumentException("Only SplitsAddition is supported by FlussSourceSplitReader");
}

Type guard

boolean isAddition(SplitsChange<?> c) { return c instanceof SplitsAddition; }

Try / catch

try {
  reader.handleSplitsChanges(change);
} catch (UnsupportedOperationException e) {
  LOG.warn("Rejected split change {} for Fluss reader", change.getClass(), e);
}

Prevention

When it happens

Trigger: handleSplitsChanges(SplitsChange<FlussSourceSplit>) receives a change instance that is not SplitsAddition — typically SplitsRemoval sent during split reassignment, failover rebalancing, or by custom enumerator logic.

Common situations: A rollback/restore scenario where the framework replays split ownership changes; custom enumerator code issuing SplitsRemoval; older SeaTunnel runtimes emitting removal changes to release splits.

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/826641a24b77171e. Report an issue: GitHub.