apache/seatunnel · error · IllegalStateException

Vitess CDC reader only supports one active streaming split.

Error message

Vitess CDC reader only supports one active streaming split.

What it means

Thrown by VitessSourceReader.addSplits when splits are added while the reader already holds an active split. Vitess CDC streams exactly one streaming split per reader, so concurrent or duplicate split assignment violates the reader's state invariant.

Solutions

  1. Ensure the split enumerator assigns at most one streaming split per reader (deregister/invalidate the reader after assignment)
  2. On restore, clear or replace the existing split instead of appending (guard/replace state under the stateLock)
  3. Update engine/checkpoint logic so addSplits is idempotent for the same split

Example fix

// before
reader.addSplits(existingSplits); // called again after restore
// after
if (reader currently holds no split) { reader.addSplits(existingSplits); }
Defensive patterns

Strategy: try-catch

Validate before calling

// caller-side: only hand each reader a split when it holds none
if (readerState.hasActiveSplit()) {
    throw new IllegalStateException("reader already has an active streaming split");
}

Type guard

boolean canAcceptSplits(VitessSourceReader r) { return !r.hasActiveSplit(); }

Try / catch

try {
    reader.addSplits(splits);
} catch (IllegalStateException e) {
    // split already active — skip duplicate assignment or replace existing split
}

Prevention

When it happens

Trigger: Calling addSplits with a non-empty list while sourceSplits is non-empty — e.g. a restore path delivering splits twice, or an enumerator assigning a second split to an already-active reader (also observed in tests testCheckpointRestorePreservesOffsetsAndTableIdentity, testSpecificStartupUsesCapturedVgtid exercising restore).

Common situations: Checkpoint/restore delivering the same split again on top of an existing one; misconfigured enumerator parallelism sending more than one split to a reader; replay of addSplits after recovery.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at seatunnel-connectors-v2/connector-cdc/connector-cdc-vitess/src/main/java/org/apache/seatunnel/connectors/seatunnel/cdc/vitess/source/reader/VitessSourceReader.java:147

    @Override
    public List<VitessSourceSplit> snapshotState(long checkpointId) {
        synchronized (stateLock) {
            if (runtime != null && !sourceSplits.isEmpty()) {
                Map<String, byte[]> runtimeTableSchemas = runtime.snapshotTableSchemas();
                if (runtimeTableSchemas != null && !runtimeTableSchemas.isEmpty()) {
                    sourceSplits.get(0).setTableSchemas(runtimeTableSchemas);
                }
            }
            return sourceSplits.stream().map(VitessSourceSplit::copy).collect(Collectors.toList());
        }
    }

    @Override
    public void addSplits(List<VitessSourceSplit> splits) {
        synchronized (stateLock) {
            if (!sourceSplits.isEmpty() && splits != null && !splits.isEmpty()) {
                throw new IllegalStateException(
                        "Vitess CDC reader only supports one active streaming split.");
            }
            if (splits != null) {
                for (VitessSourceSplit split : splits) {
                    sourceSplits.add(split.copy());
                }
            }
            if (runtime == null && !sourceSplits.isEmpty()) {
                // Start Debezium as soon as the split arrives so latest startup does not miss
                // changes produced before the framework issues the first pollNext call.
                runtime = new VitessStreamingRuntime(sourceConfig, sourceSplits.get(0));
            }
        }
    }

    @Override
    public void handleNoMoreSplits() {}

View on GitHub (pinned to cf67b549a7)