apache/flink · error · IOException

expecting more rows but reached last block. Read {} out of {

Error message

expecting more rows but reached last block. Read {} out of {}

What it means

IOException from readNextRowGroup() in ParquetColumnarRowSplitReader: the underlying ParquetFileReader.readNextRowGroup() returned null (no more row groups) while the reader still expects rows because rowsReturned has not reached totalRowCount (the row count advertised in the file footer). This is a data/metadata inconsistency: the footer promised more rows than the row groups contain.

Source

Thrown at flink-formats/flink-parquet/src/main/java/org/apache/flink/formats/parquet/vector/ParquetColumnarRowSplitReader.java:320

        if (rowsReturned == totalCountLoadedSoFar) {
            readNextRowGroup();
        }

        int num = (int) Math.min(batchSize, totalCountLoadedSoFar - rowsReturned);
        for (int i = 0; i < columnReaders.length; ++i) {
            //noinspection unchecked
            columnReaders[i].readToVector(num, writableVectors[i]);
        }
        rowsReturned += num;
        columnarBatch.setNumRows(num);
        rowsInBatch = num;
        return true;
    }

    private void readNextRowGroup() throws IOException {
        PageReadStore pages = reader.readNextRowGroup();
        if (pages == null) {
            throw new IOException(
                    "expecting more rows but reached last block. Read "
                            + rowsReturned
                            + " out of "
                            + totalRowCount);
        }
        List<Type> types = requestedSchema.getFields();
        columnReaders = new ColumnReader[types.size()];
        for (int i = 0; i < types.size(); ++i) {
            columnReaders[i] =
                    createColumnReader(
                            utcTimestamp,
                            selectedTypes[i],
                            types.get(i),
                            requestedSchema.getColumns(),
                            pages,
                            fieldList.get(i),
                            0);
        }

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Verify the file integrity: parquet-tools meta / cat on the exact split file, and re-copy or regenerate it if corrupted
  2. Re-run the job that produced the file so the footer is written atomically and completely
  3. Check object-storage consistency (S3 read-after-write, eventual consistency) if reading files immediately after upload
  4. If the file is valid, check that the split's row-count offset math (seekToRow usage) is not skipping into the middle of row groups
Defensive patterns

Strategy: try-catch

Validate before calling

ParquetMetadata md = ParquetFileReader.readFooter(conf, path);
long footerRows = md.getBlocks().stream().mapToLong(BlockMetaData::getRowCount).sum();
if (footerRows != md.getBlocks().get(md.getBlocks().size()-1).getRowCount() * 0L + footerRows) { /* sanity placeholder */ }
// real check: ensure sum of block row counts equals advertised total before reading

Try / catch

catch (IOException e) { if (e.getMessage().contains("reached last block")) { /* quarantine corrupt split, regenerate source file, then retry */ } else throw e; }

Prevention

When it happens

Trigger: readNextRowGroup() called when reader.readNextRowGroup() returns null, i.e. all row groups consumed while rowsReturned < totalRowCount. Typically triggered by truncated or corrupted files, or writers that wrote inconsistent footer row-count metadata.

Common situations: Files corrupted or truncated by interrupted writes, network copies, or faulty object storage; files produced by non-standard writers with wrong row-group row counts; combining seekToRow offsets derived from stale metadata.

Related errors


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