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
- Verify the file integrity: parquet-tools meta / cat on the exact split file, and re-copy or regenerate it if corrupted
- Re-run the job that produced the file so the footer is written atomically and completely
- Check object-storage consistency (S3 read-after-write, eventual consistency) if reading files immediately after upload
- 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
- Write Parquet atomically (temp file + rename) so footers are never half-written
- Validate files with parquet-tools cat --rows after production jobs
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
- could not decode the dictionary for {}
- totalValueCount == 0
- could not read page {} in col {}
- Corrupted Parquet schema
- Unknown ColumnIO, %s
AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14).
Data as JSON: /api/errors/69dce3e579067f16.
Report an issue: GitHub.