apache/flink · error · KeyFieldOutOfBoundsException
Field {fieldNumber} is accessed for a key, but out of bounds
Error message
Field {fieldNumber} is accessed for a key, but out of bounds in the record. What it means
In TupleComparatorBase.compareToReference, an IndexOutOfBoundsException while comparing the reference records is rethrown as KeyFieldOutOfBoundsException(keyPositions[i]). Unlike the TupleComparator paths (which read fields off actual tuples), here the exception typically escapes from a field comparator whose stored reference record is a narrower tuple/object than the configured keyPositions expect.
Source
Thrown at flink-core/src/main/java/org/apache/flink/api/java/typeutils/runtime/TupleComparatorBase.java:183
@Override
public int compareToReference(TypeComparator<T> referencedComparator) {
TupleComparatorBase<T> other = (TupleComparatorBase<T>) referencedComparator;
int i = 0;
try {
for (; i < this.keyPositions.length; i++) {
@SuppressWarnings("unchecked")
int cmp = this.comparators[i].compareToReference(other.comparators[i]);
if (cmp != 0) {
return cmp;
}
}
return 0;
} catch (NullPointerException npex) {
throw new NullKeyFieldException(keyPositions[i]);
} catch (IndexOutOfBoundsException iobex) {
throw new KeyFieldOutOfBoundsException(keyPositions[i]);
}
}
@SuppressWarnings("unchecked")
@Override
public int compareSerialized(DataInputView firstSource, DataInputView secondSource)
throws IOException {
if (deserializedFields1 == null) {
instantiateDeserializationUtils();
}
int i = 0;
try {
for (; i < serializers.length; i++) {
deserializedFields1[i] =
serializers[i].deserialize(deserializedFields1[i], firstSource);
deserializedFields2[i] =
serializers[i].deserialize(deserializedFields2[i], secondSource);View on GitHub (pinned to 2f3c205e92)
Solutions
- Re-validate keyPositions against every record type flowing through the operator; enforce a single tuple arity per operator.
- Build comparators from the stream's TypeInformation rather than hand-coding positions.
- Projection/union steps should guarantee a uniform tuple width before keyed operations.
Example fix
// before
// comparator built for Tuple3 key {2}; stream carries Tuple2
cmp.setReference(t2);
cmp.compareToReference(cmp2); // KeyFieldOutOfBoundsException(2)
// after
// project all inputs to Tuple3 first, or key on positions valid for Tuple2 (0,1)
int[] keyPositions = {1};
cmp.setReference(t2);
cmp.compareToReference(cmp2); Defensive patterns
Strategy: validation
Validate before calling
validateKeyPositions(keyPositions, sampleRecord.getArity()); // all key pos < arity cmp.setReference(sampleRecord);
Try / catch
try {
cmp.compareToReference(other);
} catch (KeyFieldOutOfBoundsException e) {
// reference record narrower than key spec: unify tuple arity upstream
} Prevention
- One tuple arity per keyed operator; project heterogeneous inputs to a common type.
- Keep keyPositions generated from the active TypeInformation.
- Regression-test comparator setup whenever tuple schemas change.
When it happens
Trigger: compareToReference after setReference stored records whose tuple arity is smaller than the comparator's key positions; the field-level comparator then indexes past the record during reference comparison and the IOOBE is wrapped with the tuple-level key position.
Common situations: Comparator reused across tuple types of different arity. Mixed-schema streams reaching one keyed operator. Hand-built keyPositions stale after a schema change (same family as 687-690).
Related errors
- Field {fieldNumber} is accessed for a key, but out of bounds
- Field {fieldNumber} is null, but expected to hold a key.
- Tuple field expression "{}" out of bounds of {}.
- {}
- Cannot create Comparator for {typeClass.getCanonicalName()}.
AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14).
Data as JSON: /api/errors/aba3b372057f9dd1.
Report an issue: GitHub.