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

TupleComparator.hash(T) computes a hash over the configured key positions of a tuple. If a key position is >= the tuple's arity, getFieldNotNull throws IndexOutOfBoundsException, which this method converts into KeyFieldFieldOutOfBoundsException wrapping the offending position. The message text ('Field {fieldNumber} is accessed for a key, but out of bounds') comes from KeyFieldOutOfBoundsException. It means the comparator's keyPositions do not match the tuple type it is applied to.

Source

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

    //  Comparator Methods
    // --------------------------------------------------------------------------------------------

    @SuppressWarnings("unchecked")
    @Override
    public int hash(T value) {
        int i = 0;
        try {
            int code = this.comparators[0].hash(value.getFieldNotNull(keyPositions[0]));
            for (i = 1; i < this.keyPositions.length; i++) {
                code *= HASH_SALT[i & 0x1F]; // salt code with (i % HASH_SALT.length)-th salt
                // component
                code += this.comparators[i].hash(value.getFieldNotNull(keyPositions[i]));
            }
            return code;
        } catch (NullFieldException nfex) {
            throw new NullKeyFieldException(nfex);
        } catch (IndexOutOfBoundsException iobex) {
            throw new KeyFieldOutOfBoundsException(keyPositions[i]);
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public void setReference(T toCompare) {
        int i = 0;
        try {
            for (; i < this.keyPositions.length; i++) {
                this.comparators[i].setReference(toCompare.getFieldNotNull(this.keyPositions[i]));
            }
        } catch (NullFieldException nfex) {
            throw new NullKeyFieldException(nfex);
        } catch (IndexOutOfBoundsException iobex) {
            throw new KeyFieldOutOfBoundsException(keyPositions[i]);
        }
    }

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Verify every entry of the keyPositions array passed to TupleComparator satisfies 0 <= pos < tuple.getArity(); fix off-by-one key positions.
  2. After any tuple schema change, rebuild the comparator from the current TypeInformation (TypeInformation.createComparator(fields, sortOrder, ExecutionConfig)) instead of hand-maintaining indices.
  3. Add a one-time assertion in job setup: for each key pos, assert pos < tupleArity, failing fast on the client rather than at runtime.

Example fix

// before
// records are Tuple2<Long, String>, but key uses field 2 (0-based) or was written 1-based
TupleComparator<Tuple2<Long,String>> cmp = new TupleComparator<>(
    new int[] {2}, new TypeComparator[] {new LongComparator()}, new TypeSerializer[] {LongSerializer.INSTANCE});
int h = cmp.hash(record); // throws KeyFieldOutOfBoundsException(2)

// after
TupleComparator<Tuple2<Long,String>> cmp = new TupleComparator<>(
    new int[] {1}, ...); // valid 0-based index, or 0 for the first field
int h = cmp.hash(record);
Defensive patterns

Strategy: validation

Validate before calling

public static void validateKeyPositions(int[] keyPositions, int tupleArity) {
    for (int pos : keyPositions) {
        if (pos < 0 || pos >= tupleArity) {
            throw new IllegalArgumentException("key position " + pos
                + " out of bounds for tuple arity " + tupleArity);
        }
    }
}

Try / catch

try {
    int h = cmp.hash(record);
} catch (org.apache.flink.types.KeyFieldOutOfBoundsException e) {
    // e.getMessage contains the offending 0-based field number; fix keyPositions
    throw new IllegalStateException("Key spec/arity mismatch: " + e.getMessage(), e);
}

Prevention

When it happens

Trigger: Constructing a TupleComparator with keyPositions like {2} and applying it (via hash-based partitioning, groupBy, join/coGroup keys on the Java Tuple API) to a Tuple2 whose valid indices are 0..1. Also when a tuple field at a valid index is fine but the comparator config was built for a wider tuple type.

Common situations: Tuple arity changed (Tuple3 -> Tuple2) but the comparator/key spec was not updated. Key position constants off by one (1-based vs 0-based confusion: Flink data model is 0-based, old Pact API was 1-based). Reusing a comparator built for one TypeInformation against records of a different tuple class.

Related errors


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