apache/flink · error · KeyFieldOutOfBoundsException

{}

Error message

{}

What it means

RowComparator.hash() iterates the configured key positions and calls record.getField(keyPositions[i]) on each. If a key position index is beyond the Row's arity (number of fields), Row.getField raises IndexOutOfBoundsException, which is caught and re-thrown as KeyFieldOutOfBoundsException with the message 'Field N is accessed for a key, but out of bounds in the record.' The comparator and the Row disagree on how many fields the Row has.

Solutions

  1. Align the key position indices with the actual Row arity — verify Row.getArity() matches the comparator's expected arity before the operation.
  2. If you changed the Row schema, update the key specification (keyBy / sort / groupBy field indices) to reference valid positions.
  3. Trace the upstream operator that produces the Row and confirm its output arity; add an arity check in a MapFunction if needed.
  4. If restoring from a savepoint with a schema mismatch, implement a state migration or start from a clean savepoint.

Example fix

// before — comparator keys on position 2 but Rows only have 2 fields (indices 0,1)
dataSet.sortPartition(2, Order.ASCENDING); // Row arity = 2 → IOOBE

// after — key on a valid position within the Row's arity
dataSet.sortPartition(1, Order.ASCENDING); // valid: index 1 < arity 2
Defensive patterns

Strategy: validation

Validate before calling

// Before hashing or keying a Row, validate all key positions are in bounds
public static void validateKeyPositions(Row row, int[] keyPositions) {
    for (int pos : keyPositions) {
        if (pos < 0 || pos >= row.getArity()) {
            throw new IllegalArgumentException(
                "Key position " + pos + " is out of bounds for Row arity " + row.getArity());
        }
    }
}

Try / catch

try {
    int hash = comparator.hash(record);
} catch (KeyFieldOutOfBoundsException e) {
    log.error("Row arity {} too small for key position {}",
        record.getArity(), e.getFieldNumber());
    throw e;
}

Prevention

When it happens

Trigger: The RowComparator is configured with keyPositions referencing field indices that the actual Row at runtime does not contain — e.g., comparator keys on position 3 but the Row only has 2 fields. This arises when the comparator's arity assumption diverges from the data's actual arity.

Common situations: The Row schema was changed (fields added or removed) without updating the key specification; a keyBy / sort uses positional indices computed against one schema but the upstream operator emits Rows with a different arity; a DataSet groupBy/sort with key positions [0,1,2] receives Rows of arity 2 due to a projection that was added without updating keys; legacy savepoint data with an older Row arity restored into a job whose comparator expects more fields.

Related errors


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

Appendix: source

Thrown at flink-core/src/main/java/org/apache/flink/api/java/typeutils/runtime/RowComparator.java:181

    public void getFlatComparator(List<TypeComparator> flatComparators) {
        for (NullAwareComparator<Object> c : comparators) {
            Collections.addAll(flatComparators, c.getFlatComparators());
        }
    }

    @Override
    public int hash(Row record) {
        int code = 0;
        int i = 0;

        try {
            for (; i < keyPositions.length; i++) {
                code *= TupleComparatorBase.HASH_SALT[i & 0x1F];
                Object element = record.getField(keyPositions[i]); // element can be null
                code += comparators[i].hash(element);
            }
        } catch (IndexOutOfBoundsException e) {
            throw new KeyFieldOutOfBoundsException(keyPositions[i]);
        }

        return code;
    }

    @Override
    public void setReference(Row toCompare) {
        int i = 0;
        try {
            for (; i < keyPositions.length; i++) {
                TypeComparator<Object> comparator = comparators[i];
                Object element = toCompare.getField(keyPositions[i]);
                comparator.setReference(element); // element can be null
            }
        } catch (IndexOutOfBoundsException e) {
            throw new KeyFieldOutOfBoundsException(keyPositions[i]);
        }
    }

View on GitHub (pinned to 2f3c205e92)