apache/flink · error · UnsupportedOperationException

Only support seek at first.

Error message

Only support seek at first.

What it means

UnsupportedOperationException from seekToRow(long) in ParquetColumnarRowSplitReader: seeking is only allowed before any row group has been loaded. The guard is totalCountLoadedSoFar != 0, which becomes non-zero as soon as a row group is read or a previous seek skipped row groups. The design is one-shot: position once, then stream forward.

Source

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

        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);
        }
        totalCountLoadedSoFar += pages.getRowCount();
    }

    /** Seek to a particular row number. */
    public void seekToRow(long rowCount) throws IOException {
        if (totalCountLoadedSoFar != 0) {
            throw new UnsupportedOperationException("Only support seek at first.");
        }

        List<BlockMetaData> blockMetaData = reader.getRowGroups();

        for (BlockMetaData metaData : blockMetaData) {
            if (metaData.getRowCount() > rowCount) {
                break;
            } else {
                reader.skipNextRowGroup();
                rowsReturned += metaData.getRowCount();
                totalCountLoadedSoFar += metaData.getRowCount();
                rowsInBatch = (int) metaData.getRowCount();
                nextRow = (int) metaData.getRowCount();
                rowCount -= metaData.getRowCount();
            }
        }
        for (int i = 0; i < rowCount; i++) {
            boolean end = reachedEnd();

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Create a fresh ParquetColumnarRowSplitReader per seek: close the old one, reopen on the same InputFile, then seekToRow
  2. Restructure the consumer to do a single seek up front and only read forward afterwards
  3. For repeated random access, cache the rows you need instead of re-seeking the streaming reader

Example fix

// before
reader.seekToRow(100);
... read ...
reader.seekToRow(500); // UnsupportedOperationException

// after
reader.close();
reader = newReader(inputFile, ...);
reader.seekToRow(500);
Defensive patterns

Strategy: validation

Validate before calling

if (reader.hasLoadedData()) { throw new IllegalStateException("seekToRow only valid before reading - recreate the reader"); } // or track your own 'readStarted' flag

Prevention

When it happens

Trigger: Calling seekToRow(rowCount) a second time, or calling it after nextRecord()/reachedEnd() has already caused a row group to load, on the same reader instance.

Common situations: Reusing a reader for random access (e.g. implementing sampling or offset-based retries); upper layers assuming a seekable cursor API like a JDBC result set; iterator wrappers that rewind and re-seek on the same split.

Related errors


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