{"record":{"id":"fd6af4f18fb5c34a","repo":"apache/flink","slug":"cannot-fetch-from-another-split-no-split-remaini","errorCode":null,"errorMessage":"Cannot fetch from another split - no split remaining","messagePattern":"Cannot fetch from another split - no split remaining","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"flink-connectors/flink-connector-files/src/main/java/org/apache/flink/connector/file/src/impl/FileSourceSplitReader.java","lineNumber":103,"sourceCode":"\n    @Override\n    public void wakeUp() {}\n\n    @Override\n    public void close() throws Exception {\n        if (currentReader != null) {\n            currentReader.close();\n        }\n    }\n\n    private void checkSplitOrStartNext() throws IOException {\n        if (currentReader != null) {\n            return;\n        }\n\n        final SplitT nextSplit = splits.poll();\n        if (nextSplit == null) {\n            throw new IOException(\"Cannot fetch from another split - no split remaining\");\n        }\n\n        currentSplitId = nextSplit.splitId();\n\n        final Optional<CheckpointedPosition> position = nextSplit.getReaderPosition();\n        currentReader =\n                position.isPresent()\n                        ? readerFactory.restoreReader(config, nextSplit)\n                        : readerFactory.createReader(config, nextSplit);\n    }\n\n    private FileRecords<T> finishSplit() throws IOException {\n        if (currentReader != null) {\n            currentReader.close();\n            currentReader = null;\n        }\n\n        final FileRecords<T> finishRecords = FileRecords.finishedSplit(currentSplitId);","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/apache/flink/blob/2f3c205e9266cb30240eb7f4fdab15cad629a70f/flink-connectors/flink-connector-files/src/main/java/org/apache/flink/connector/file/src/impl/FileSourceSplitReader.java#L85-L121","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","In custom reader wrappers, check for available splits before calling fetch(); propagate the no-more-splits signal correctly.","In tests, ensure splits are added to the reader via addSplits before calling fetch()."],"exampleFix":"// before\n// reader.fetch() called with no splits in queue\n\n// after\nreader.addSplits(Collections.singletonList(split));\n// then call fetch()\n// or check if reader has finished all splits before calling fetch()","handlingStrategy":"validation","validationCode":"// Before calling fetch(), verify the reader has splits or a current reader\n// This is framework-managed; in custom wrappers, track split availability:\nif (reader.getCurrentSplitId() == null && !reader.hasSplitsAvailable()) {\n    // do not call fetch(); signal completion instead\n    return;\n}","typeGuard":null,"tryCatchPattern":"try {\n    return reader.fetch();\n} catch (IOException e) {\n    if (e.getMessage().contains(\"no split remaining\")) {\n        // No more splits to process; signal completion\n        return null; // or appropriate empty records\n    }\n    throw e;\n}","preventionTips":["In custom reader wrappers, track whether splits remain before calling fetch().","Ensure the enumerator assigns splits before the reader is polled.","In tests, always add splits via addSplits() before calling fetch()."],"tags":["file-source-reader","split-exhaustion","io-exception","flink"],"backgroundTag":null,"analyzedSha":"2f3c205e9266cb30240eb7f4fdab15cad629a70f","analyzedAt":"2026-08-14T08:48:24.518Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}