{"record":{"id":"1352d658913d2cf4","repo":"apache/hadoop","slug":"split-points-are-out-of-order","errorCode":null,"errorMessage":"Split points are out of order","messagePattern":"Split points are out of order","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/lib/partition/TotalOrderPartitioner.java","lineNumber":95,"sourceCode":"    try {\n      this.conf = conf;\n      String parts = getPartitionFile(conf);\n      final Path partFile = new Path(parts);\n      final FileSystem fs = (DEFAULT_PATH.equals(parts))\n        ? FileSystem.getLocal(conf)     // assume in DistributedCache\n        : partFile.getFileSystem(conf);\n\n      Job job = Job.getInstance(conf);\n      Class<K> keyClass = (Class<K>)job.getMapOutputKeyClass();\n      K[] splitPoints = readPartitions(fs, partFile, keyClass, conf);\n      if (splitPoints.length != job.getNumReduceTasks() - 1) {\n        throw new IOException(\"Wrong number of partitions in keyset\");\n      }\n      RawComparator<K> comparator =\n        (RawComparator<K>) job.getSortComparator();\n      for (int i = 0; i < splitPoints.length - 1; ++i) {\n        if (comparator.compare(splitPoints[i], splitPoints[i+1]) >= 0) {\n          throw new IOException(\"Split points are out of order\");\n        }\n      }\n      boolean natOrder =\n        conf.getBoolean(NATURAL_ORDER, true);\n      if (natOrder && BinaryComparable.class.isAssignableFrom(keyClass)) {\n        partitions = buildTrie((BinaryComparable[])splitPoints, 0,\n            splitPoints.length, new byte[0],\n            // Now that blocks of identical splitless trie nodes are \n            // represented reentrantly, and we develop a leaf for any trie\n            // node with only one split point, the only reason for a depth\n            // limit is to refute stack overflow or bloat in the pathological\n            // case where the split points are long and mostly look like bytes \n            // iii...iixii...iii   .  Therefore, we make the default depth\n            // limit large but not huge.\n            conf.getInt(MAX_TRIE_DEPTH, 200));\n      } else {\n        partitions = new BinarySearchNode(splitPoints, comparator);\n      }","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/lib/partition/TotalOrderPartitioner.java#L77-L113","documentation":"After the count check, TotalOrderPartitioner.setConf validates that split points strictly increase under the job's sort comparator: compare(splitPoints[i], splitPoints[i+1]) >= 0 for any adjacent pair throws this IOException. Equal or descending neighbours mean the file is not a strictly sorted sequence of cut points.","triggerScenarios":"Duplicate keys among the sampled points (low-cardinality key space with RandomSampler/HashSampler); a partition file written with a different key class or comparator ordering than the running job uses (e.g. custom RawComparator vs natural Text ordering); hand-written file not sorted.","commonSituations":"Sampling a key column with very few distinct values so the same cut point appears twice; switching the map output key class or comparator after the file was generated; sorting the file with the wrong collation (locale-aware sort on the shell).","solutions":["Dedupe the split points, then re-check the count equals numReduceTasks-1","Increase sample size (more samples / higher frequency in InputSampler) so distinct cut points exist","Ensure the job's map output key class and sort comparator are identical to those used when the partition file was written"],"exampleFix":"// before: too few samples on a low-cardinality key -> duplicate points\nnew InputSampler.RandomSampler<>(0.01, 10);\n// after: larger sample, then dedupe and match count to R-1\nnew InputSampler.RandomSampler<>(0.5, 10000);","handlingStrategy":"validation","validationCode":"RawComparator<Text> cmp = (RawComparator<Text>) job.getSortComparator();\nList<Text> points = readSplitPoints(partFile); // same deserialization as readPartitions\nfor (int i = 0; i + 1 < points.size(); i++) {\n  if (cmp.compare(points.get(i), points.get(i + 1)) >= 0) {\n    throw new IOException(\"Split points not strictly increasing at index \" + i);\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  TotalOrderPartitioner<Text, Text> p = new TotalOrderPartitioner<>();\n  p.setConf(job.getConfiguration());\n} catch (IllegalArgumentException | IOException e) {\n  throw new RuntimeException(\"Split points invalid: \" + e.getMessage(), e);\n}","preventionTips":["Dedupe sampled cut points and verify count equals R-1 before submit","Keep the key class and comparator identical between sampling and the job","Check sample cardinality: low-distinct keys need a different partitioning strategy"],"tags":["mapreduce","partitioning","total-order-sort","sampling","sorting"],"backgroundTag":"sort-order-violation","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}