{"record":{"id":"53c8966250661a10","repo":"apache/flink","slug":"field-fieldnumber-is-accessed-for-a-key-but-out","errorCode":null,"errorMessage":"Field {fieldNumber} is accessed for a key, but out of bounds in the record.","messagePattern":"Field (.+?) is accessed for a key, but out of bounds in the record\\.","errorType":"exception","errorClass":"KeyFieldOutOfBoundsException","httpStatus":null,"severity":"error","filePath":"flink-core/src/main/java/org/apache/flink/api/java/typeutils/runtime/TupleComparator.java","lineNumber":63,"sourceCode":"    //  Comparator Methods\n    // --------------------------------------------------------------------------------------------\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public int hash(T value) {\n        int i = 0;\n        try {\n            int code = this.comparators[0].hash(value.getFieldNotNull(keyPositions[0]));\n            for (i = 1; i < this.keyPositions.length; i++) {\n                code *= HASH_SALT[i & 0x1F]; // salt code with (i % HASH_SALT.length)-th salt\n                // component\n                code += this.comparators[i].hash(value.getFieldNotNull(keyPositions[i]));\n            }\n            return code;\n        } catch (NullFieldException nfex) {\n            throw new NullKeyFieldException(nfex);\n        } catch (IndexOutOfBoundsException iobex) {\n            throw new KeyFieldOutOfBoundsException(keyPositions[i]);\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public void setReference(T toCompare) {\n        int i = 0;\n        try {\n            for (; i < this.keyPositions.length; i++) {\n                this.comparators[i].setReference(toCompare.getFieldNotNull(this.keyPositions[i]));\n            }\n        } catch (NullFieldException nfex) {\n            throw new NullKeyFieldException(nfex);\n        } catch (IndexOutOfBoundsException iobex) {\n            throw new KeyFieldOutOfBoundsException(keyPositions[i]);\n        }\n    }\n","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/apache/flink/blob/2f3c205e9266cb30240eb7f4fdab15cad629a70f/flink-core/src/main/java/org/apache/flink/api/java/typeutils/runtime/TupleComparator.java#L45-L81","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify every entry of the keyPositions array passed to TupleComparator satisfies 0 <= pos < tuple.getArity(); fix off-by-one key positions.","After any tuple schema change, rebuild the comparator from the current TypeInformation (TypeInformation.createComparator(fields, sortOrder, ExecutionConfig)) instead of hand-maintaining indices.","Add a one-time assertion in job setup: for each key pos, assert pos < tupleArity, failing fast on the client rather than at runtime."],"exampleFix":"// before\n// records are Tuple2<Long, String>, but key uses field 2 (0-based) or was written 1-based\nTupleComparator<Tuple2<Long,String>> cmp = new TupleComparator<>(\n    new int[] {2}, new TypeComparator[] {new LongComparator()}, new TypeSerializer[] {LongSerializer.INSTANCE});\nint h = cmp.hash(record); // throws KeyFieldOutOfBoundsException(2)\n\n// after\nTupleComparator<Tuple2<Long,String>> cmp = new TupleComparator<>(\n    new int[] {1}, ...); // valid 0-based index, or 0 for the first field\nint h = cmp.hash(record);","handlingStrategy":"validation","validationCode":"public static void validateKeyPositions(int[] keyPositions, int tupleArity) {\n    for (int pos : keyPositions) {\n        if (pos < 0 || pos >= tupleArity) {\n            throw new IllegalArgumentException(\"key position \" + pos\n                + \" out of bounds for tuple arity \" + tupleArity);\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    int h = cmp.hash(record);\n} catch (org.apache.flink.types.KeyFieldOutOfBoundsException e) {\n    // e.getMessage contains the offending 0-based field number; fix keyPositions\n    throw new IllegalStateException(\"Key spec/arity mismatch: \" + e.getMessage(), e);\n}","preventionTips":["Treat key positions as 0-based everywhere; document the convention at each call site.","Build comparators via TypeInformation.createComparator instead of hand-coded arrays.","Unit-test hash()/compare() with a representative record before wiring into a job."],"tags":["tuple","key-position","out-of-bounds","comparator","partitioning"],"backgroundTag":null,"analyzedSha":"2f3c205e9266cb30240eb7f4fdab15cad629a70f","analyzedAt":"2026-08-14T08:48:24.518Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}