apache/flink · error · IOException

Cannot fetch from another split - no split remaining

Error message

Cannot fetch from another split - no split remaining

What it means

FileSourceSplitReader.checkSplitOrStartNext polls the internal split queue when the current reader is exhausted or null. If the queue is empty (splits.poll() returns null), it throws an IOException indicating there are no more splits to process. This is a control-flow signal that the reader has consumed all assigned splits.

Source

Thrown at flink-connectors/flink-connector-files/src/main/java/org/apache/flink/connector/file/src/impl/FileSourceSplitReader.java:103

    @Override
    public void wakeUp() {}

    @Override
    public void close() throws Exception {
        if (currentReader != null) {
            currentReader.close();
        }
    }

    private void checkSplitOrStartNext() throws IOException {
        if (currentReader != null) {
            return;
        }

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

        currentSplitId = nextSplit.splitId();

        final Optional<CheckpointedPosition> position = nextSplit.getReaderPosition();
        currentReader =
                position.isPresent()
                        ? readerFactory.restoreReader(config, nextSplit)
                        : readerFactory.createReader(config, nextSplit);
    }

    private FileRecords<T> finishSplit() throws IOException {
        if (currentReader != null) {
            currentReader.close();
            currentReader = null;
        }

        final FileRecords<T> finishRecords = FileRecords.finishedSplit(currentSplitId);

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Ensure the enumerator assigns splits or signals no-more-splits before the reader exhausts its queue. Check that the enumerator's handleSplitRequest properly assigns splits.
  2. In custom reader wrappers, check for available splits before calling fetch(); propagate the no-more-splits signal correctly.
  3. In tests, ensure splits are added to the reader via addSplits before calling fetch().

Example fix

// before
// reader.fetch() called with no splits in queue

// after
reader.addSplits(Collections.singletonList(split));
// then call fetch()
// or check if reader has finished all splits before calling fetch()
Defensive patterns

Strategy: validation

Validate before calling

// Before calling fetch(), verify the reader has splits or a current reader
// This is framework-managed; in custom wrappers, track split availability:
if (reader.getCurrentSplitId() == null && !reader.hasSplitsAvailable()) {
    // do not call fetch(); signal completion instead
    return;
}

Try / catch

try {
    return reader.fetch();
} catch (IOException e) {
    if (e.getMessage().contains("no split remaining")) {
        // No more splits to process; signal completion
        return null; // or appropriate empty records
    }
    throw e;
}

Prevention

When it happens

Trigger: fetch() is called after all assigned splits have been consumed (currentReader is null and splits queue is empty). This can happen if the source reader is polled after it has finished all work but before the enumerator has signaled no-more-splits, or in a custom integration that calls fetch() without first providing splits.

Common situations: Reader polled after all splits processed but framework has not yet terminated the task. Custom source reader wrapper calling fetch() without ensuring splits are available. Race condition in testing where the reader is exercised before splits are assigned.

Related errors


AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14). Data as JSON: /api/errors/fd6af4f18fb5c34a. Report an issue: GitHub.