apache/flink · error · KeyFieldOutOfBoundsException

Field is accessed for a key, but out of bounds in the…

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.

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.

Appendix: 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)