apache/flink · error · RuntimeException

You must have forgotten to call open() on your input format.

Error message

You must have forgotten to call open() on your input format.

What it means

BinaryInputFormat.getCurrentState() (used for checkpointing the input format's progress) reads position and record count from the internal blockBasedInput. That field is only initialized by open(FileInputSplit). If getCurrentState is called before open completes (blockBasedInput == null), the format throws RuntimeException with a 'forgot to call open()' message, because there is no valid checkpoint state to return.

Source

Thrown at flink-core/src/main/java/org/apache/flink/api/common/io/BinaryInputFormat.java:397

                offset += read;
                if (this.blockPos >= this.maxPayloadSize) {
                    this.skipHeader();
                }
                remainingLength -= read;
            }
            return totalRead;
        }
    }

    // --------------------------------------------------------------------------------------------
    //  Checkpointing
    // --------------------------------------------------------------------------------------------

    @PublicEvolving
    @Override
    public Tuple2<Long, Long> getCurrentState() throws IOException {
        if (this.blockBasedInput == null) {
            throw new RuntimeException(
                    "You must have forgotten to call open() on your input format.");
        }

        return new Tuple2<>(
                this.blockBasedInput.getCurrBlockPos(), // the last read index in the block
                this.readRecords // the number of records read
                );
    }

    @PublicEvolving
    @Override
    public void reopen(FileInputSplit split, Tuple2<Long, Long> state) throws IOException {
        Preconditions.checkNotNull(split, "reopen() cannot be called on a null split.");
        Preconditions.checkNotNull(state, "reopen() cannot be called with a null initial state.");

        try {
            this.open(split);
        } finally {

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Ensure open(split) is called and completes before any getCurrentState()/reopen() invocation (the runtime normally guarantees this).
  2. In custom drivers/tests, follow the lifecycle: configure -> open(split) -> [nextRecord...] -> getCurrentState() -> reopen(split, state).
  3. If writing a custom source wrapping this format, defer/skip the checkpoint until the format reports it is open.

Example fix

// before
Tuple2<Long,Long> state = format.getCurrentState(); // blockBasedInput == null -> throws

// after
format.open(split);
// ... read records ...
Tuple2<Long,Long> state = format.getCurrentState();
Defensive patterns

Strategy: validation

Validate before calling

// Only checkpoint state after open has initialized the block input.
if (format.getBlockBasedInput() != null) { // assumes accessor or package-private visibility
    Tuple2<Long, Long> state = format.getCurrentState();
} else {
    // skip / return empty state
}

Try / catch

try {
    Tuple2<Long, Long> state = format.getCurrentState();
} catch (RuntimeException e) {
    if (e.getMessage() != null && e.getMessage().contains("call open()")) {
        // format not opened yet; no checkpoint state to store
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Invoking format.getCurrentState() before format.open(split); or a checkpoint trigger firing before the source operator opened its split; or a custom driver/test that calls the CheckpointableInputFormat lifecycle out of order.

Common situations: Manual unit testing of checkpointable input formats without driving open() first; a race where a checkpoint barrier arrives during operator initialization before open() finished; incorrect custom source wrapping a BinaryInputFormat.

Related errors


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