{"record":{"id":"f024954184475f34","repo":"MyCATApache/Mycat-Server","slug":"comparison-method-violates-its-general-contract","errorCode":null,"errorMessage":"Comparison method violates its general contract!","messagePattern":"Comparison method violates its general contract!","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"src/main/java/io/mycat/memory/unsafe/utils/sort/TimSort.java","lineNumber":792,"sourceCode":"              break outer;\n          }\n          s.copyElement(tmp, cursor1++, a, dest++);\n          if (--len1 == 1)\n            break outer;\n          minGallop--;\n        } while (count1 >= MIN_GALLOP | count2 >= MIN_GALLOP);\n        if (minGallop < 0)\n          minGallop = 0;\n        minGallop += 2;  // Penalize for leaving gallop mode\n      }  // End of \"outer\" loop\n      this.minGallop = minGallop < 1 ? 1 : minGallop;  // Write back to field\n\n      if (len1 == 1) {\n        assert len2 > 0;\n        s.copyRange(a, cursor2, a, dest, len2);\n        s.copyElement(tmp, cursor1, a, dest + len2); //  Last elt of run 1 to end of merge\n      } else if (len1 == 0) {\n        throw new IllegalArgumentException(\n            \"Comparison method violates its general contract!\");\n      } else {\n        assert len2 == 0;\n        assert len1 > 1;\n        s.copyRange(tmp, cursor1, a, dest, len1);\n      }\n    }\n\n    /**\n     * Like mergeLo, except that this method should be called only if\n     * len1 >= len2; mergeLo should be called if len1 <= len2.  (Either method\n     * may be called if len1 == len2.)\n     *\n     * @param base1 index of first element in first run to be merged\n     * @param len1  length of first run to be merged (must be > 0)\n     * @param base2 index of first element in second run to be merged\n     *        (must be aBase + aLen)\n     * @param len2  length of second run to be merged (must be > 0)","sourceCodeStart":774,"sourceCodeEnd":810,"githubUrl":"https://github.com/MyCATApache/Mycat-Server/blob/65f8d8beb752f935752f2a0eec0ab017facab9ef/src/main/java/io/mycat/memory/unsafe/utils/sort/TimSort.java#L774-L810","documentation":"TimSort.mergeLo throws IllegalArgumentException(\"Comparison method violates its general contract!\") when a merge consumes all of run1's remaining elements (len1 reaches 0) in a way that indicates the comparator is inconsistent — typically one that violates the transitivity or antisymmetry contract of Comparator.compare. Java's (and this ported) TimSort detects the corruption defensively.","triggerScenarios":"Sorting with a comparator whose result is inconsistent across calls: it depends on mutable object state that changes during the sort, returns contradictory results (compare(a,b) and compare(b,a) both positive), or is not transitive.","commonSituations":"Comparators that call Math.random() or read fields mutated concurrently by another thread; multi-key comparators with inconsistent tie-breaking (e.g. comparing a-b on one key but b-a on another); double subtraction overflow (int)(a - b) for large values.","solutions":["Fix the comparator to be a total order: sign-consistent, transitive, and stable during the sort.","Never compare with subtraction on ints/longs; use Integer.compare/Long.compare to avoid overflow.","Stop mutating the elements (or fields used by the comparator) while sorting; sort a snapshot.","Ensure multi-field comparators fall back consistently, e.g. thenComparing chains with the same direction.","As a temporary workaround, run with the legacy merge sort flag (useLegacyMergeSort) while fixing the comparator."],"exampleFix":"// before (inconsistent, overflow-prone)\npublic int compare(Node a, Node b) { return (int)(a.score - b.score) ; }\n// after\npublic int compare(Node a, Node b) { return Long.compare(a.score, b.score); }","handlingStrategy":"validation","validationCode":"// verify transitivity before sorting\nstatic <T> boolean isConsistent(Comparator<T> c, List<T> sample) {\n  for (T a : sample) for (T b : sample) for (T d : sample)\n    if (c.compare(a,b) < 0 && c.compare(b,d) < 0 && c.compare(a,d) > 0) return false;\n  return true;\n}","typeGuard":null,"tryCatchPattern":"try { Collections.sort(list, cmp); } catch (IllegalArgumentException e) { if (e.getMessage().contains(\"general contract\")) { list.sort(Comparator.comparingLong(Key::of)); } else throw e; }","preventionTips":["Use Integer.compare/Long.compare instead of subtraction","Never mutate elements while sorting","Write transitivity unit tests for custom comparators","Avoid comparator logic depending on random or time-varying state"],"tags":["sorting","comparator","timsort"],"backgroundTag":"comparator-violates-contract","analyzedSha":"65f8d8beb752f935752f2a0eec0ab017facab9ef","analyzedAt":"2026-09-11T00:12:21.696Z","contentChangedAt":"2026-09-11T00:12:21.696Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}