{"record":{"id":"d75ebcf270736d75","repo":"apache/flink","slug":"number-of-key-fields-and-comparators-differ","errorCode":null,"errorMessage":"Number of key fields and comparators differ.","messagePattern":"Number of key fields and comparators differ\\.","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"flink-core/src/main/java/org/apache/flink/api/common/typeutils/GenericPairComparator.java","lineNumber":49,"sourceCode":"    private final TypeComparator<T1> comparator1;\n    private final TypeComparator<T2> comparator2;\n\n    private final TypeComparator<Object>[] comparators1;\n    private final TypeComparator<Object>[] comparators2;\n\n    private final Object[] referenceKeyFields;\n\n    private final Object[] candidateKeyFields;\n\n    @SuppressWarnings(\"unchecked\")\n    public GenericPairComparator(TypeComparator<T1> comparator1, TypeComparator<T2> comparator2) {\n        this.comparator1 = comparator1;\n        this.comparator2 = comparator2;\n        this.comparators1 = comparator1.getFlatComparators();\n        this.comparators2 = comparator2.getFlatComparators();\n\n        if (comparators1.length != comparators2.length) {\n            throw new IllegalArgumentException(\"Number of key fields and comparators differ.\");\n        }\n\n        int numKeys = comparators1.length;\n\n        for (int i = 0; i < numKeys; i++) {\n            this.comparators1[i] = comparators1[i].duplicate();\n            this.comparators2[i] = comparators2[i].duplicate();\n        }\n\n        this.referenceKeyFields = new Object[numKeys];\n        this.candidateKeyFields = new Object[numKeys];\n    }\n\n    @Override\n    public void setReference(T1 reference) {\n        comparator1.extractKeys(reference, referenceKeyFields, 0);\n    }\n","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/apache/flink/blob/2f3c205e9266cb30240eb7f4fdab15cad629a70f/flink-core/src/main/java/org/apache/flink/api/common/typeutils/GenericPairComparator.java#L31-L67","documentation":"Thrown by GenericPairComparator's constructor when comparator1.getFlatComparators() and comparator2.getFlatComparators() return arrays of different lengths. The GenericPairComparator is used to compare keys between two sides of a join/co-group; both sides must project to the same number of flat (atomic) key fields for the comparison to be meaningful.","triggerScenarios":"Joining or co-grouping two DataStreams/DataSets where the key selectors produce types with a different number of flattened key fields. For example, joining on a single-field key on one side and a two-field composite key on the other. Also triggered when one side's key type is a Tuple2 (2 flat fields) and the other's is a single String (1 flat field).","commonSituations":"keyBy with different key arities on two streams before a join. Using a POJO key on one side and a Tuple key on the other where the flattened field counts differ. A key selector returning a nested object on one side and a flat scalar on the other.","solutions":["Ensure both sides of the join/co-group use key selectors that produce the same number of flat key fields.","If one side needs a composite key, wrap the other side in a Tuple of matching arity.","Use a KeySelector that returns a Tuple or POJO with identical field structure on both sides."],"exampleFix":"// before — mismatched key arity on a join\nstreamA.keyBy(e -> e.getId())              // 1 flat field\n      .join(streamB.keyBy(e -> new Tuple2<>(e.getA(), e.getB()))) // 2 flat fields\n// throws: Number of key fields and comparators differ.\n\n// after — match key arity on both sides\nstreamA.keyBy(e -> new Tuple2<>(e.getCategory(), e.getId()))\n      .join(streamB.keyBy(e -> new Tuple2<>(e.getA(), e.getB())))","handlingStrategy":"validation","validationCode":"// Verify both comparators have the same number of flat key fields before joining\nTypeComparator<?> c1 = typeInfo1.createComparator(new int[]{0}, new boolean[]{true}, 0, config);\nTypeComparator<?> c2 = typeInfo2.createComparator(new int[]{0, 1}, new boolean[]{true, true}, 0, config);\nif (c1.getFlatComparators().length != c2.getFlatComparators().length) {\n    throw new IllegalArgumentException(\"Key arities differ: \"\n        + c1.getFlatComparators().length + \" vs \" + c2.getFlatComparators().length);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Ensure both sides of a join/co-group use KeySelectors that produce the same number of flat key fields.","Wrap scalar keys in a Tuple to match a composite key's arity on the other side.","Unit-test key selectors to confirm flat field counts align."],"tags":["comparator","join","co-group","key-selector","illegal-argument"],"backgroundTag":null,"analyzedSha":"2f3c205e9266cb30240eb7f4fdab15cad629a70f","analyzedAt":"2026-08-14T08:48:24.518Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}