apache/seatunnel · critical · IOException

Cannot fetch from another split - no split remaining.

Error message

Cannot fetch from another split - no split remaining.

What it means

checkSplitOrStartNext() pops the next split from the queued splits when it can assign a new one; the queue returned null/empty despite canAssignNextSplit() being true, which is an impossible/invalid internal state (no split remaining to fetch from). Thrown as IOException to fail the reader task.

Source

Thrown at seatunnel-connectors-v2/connector-cdc/connector-cdc-base/src/main/java/org/apache/seatunnel/connectors/cdc/base/source/reader/IncrementalSourceSplitReader.java:161

            currentSplitId = null;
            emittedFinishedSplitId = null;
        }
    }

    private void checkNeedStopBinlogReader() {
        // TODO Currently not supported
    }

    protected void checkSplitOrStartNext() throws IOException {
        // the stream fetcher should keep alive
        if (currentFetcher instanceof IncrementalSourceStreamFetcher) {
            return;
        }

        if (canAssignNextSplit()) {
            final SourceSplitBase nextSplit = splits.poll();
            if (nextSplit == null) {
                throw new IOException("Cannot fetch from another split - no split remaining.");
            }
            currentSplitId = nextSplit.splitId();
            emittedFinishedSplitId = null;

            if (nextSplit.isSnapshotSplit()) {
                if (currentFetcher == null) {
                    final FetchTask.Context taskContext =
                            dataSourceDialect.createFetchTaskContext(nextSplit, sourceConfig);
                    currentFetcher = new IncrementalSourceScanFetcher(taskContext, subtaskId);
                }
            } else {
                // point from snapshot split to incremental split
                if (currentFetcher != null) {
                    log.info(
                            "It's turn to read incremental split, close current snapshot fetcher.");
                    currentFetcher.close();
                }
                final FetchTask.Context taskContext =

View on GitHub (pinned to cf67b549a7)

Solutions

  1. Restart the job from the last successful checkpoint to resynchronize split state
  2. Enable debug logging of handleSplitsChanges/checkSplitOrStartNext to confirm splits were actually sent to this reader
  3. Verify the enumerator is alive and assigning splits (check for table-discovery or assignment errors upstream)
  4. Upgrade SeaTunnel/connector version if this matches a known split-assignment race bug
Defensive patterns

Strategy: try-catch

Validate before calling

// Before fetch: confirm a split is queued or a split is active
if (canAssignNextSplit() && splits.isEmpty() && currentSplitId == null) {
    LOG.warn("No split available; skipping fetch cycle instead of polling null");
    return;
}

Try / catch

try { checkSplitOrStartNext(); }
catch (IOException e) {
  if (e.getMessage().contains("no split remaining")) { restartFromLastCheckpoint(); }
  else { throw e; }
}

Prevention

When it happens

Trigger: fetch() -> checkSplitOrStartNext(): canAssignNextSplit() returns true (e.g. currentSplitId is null and no finished-split is pending) but splits.poll() yields null — meaning the reader believes it should take a new split while none was delivered.

Common situations: Enumerator and reader state divergence after checkpoint restore; splits consumed but reader state (emittedFinishedSplitId/currentSplitId) not updated consistently; engine bug in split delivery order.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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