{"record":{"id":"6df6bf4a0f736774","repo":"apache/hadoop","slug":"iterate-past-last-value","errorCode":null,"errorMessage":"iterate past last value","messagePattern":"iterate past last value","errorType":"exception","errorClass":"NoSuchElementException","httpStatus":null,"severity":"error","filePath":"hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/Task.java","lineNumber":1625,"sourceCode":"      this.keyDeserializer.open(keyIn);\n      this.valDeserializer = serializationFactory.getDeserializer(valClass);\n      this.valDeserializer.open(this.valueIn);\n      readNextKey();\n      key = nextKey;\n      nextKey = null; // force new instance creation\n      hasNext = more;\n    }\n\n    RawKeyValueIterator getRawIterator() { return in; }\n    \n    /// Iterator methods\n\n    public boolean hasNext() { return hasNext; }\n\n    private int ctr = 0;\n    public VALUE next() {\n      if (!hasNext) {\n        throw new NoSuchElementException(\"iterate past last value\");\n      }\n      try {\n        readNextValue();\n        readNextKey();\n      } catch (IOException ie) {\n        throw new RuntimeException(\"problem advancing post rec#\"+ctr, ie);\n      }\n      reporter.progress();\n      return value;\n    }\n\n    public void remove() { throw new RuntimeException(\"not implemented\"); }\n\n    /// Auxiliary methods\n\n    /** Start processing next unique key. */\n    public void nextKey() throws IOException {\n      // read until we find a new key","sourceCodeStart":1607,"sourceCodeEnd":1643,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/Task.java#L1607-L1643","documentation":"ReduceValuesIterator (Task.ValuesIterator) is the old-API iterator over all values of the current reduce key, backed by the sorted merge stream. next() first checks the cached hasNext flag and throws NoSuchElementException if the iterator is exhausted. Calling next() without a preceding true from hasNext() — or continuing after the loop — triggers it.","triggerScenarios":"Calling next() twice per iteration inside reduce(), caching the iterator and reusing it after exhaustion, or hand-rolled while(true) loops that skip the hasNext() check on an edge case (empty value list for a key).","commonSituations":"Custom reduce logic that peeks ahead ('look at the next value') and forgets the boundary; secondary-sort patterns where user code steps the iterator manually; converting for-each loops to manual iteration.","solutions":["Call next() only immediately after hasNext() returned true.","Prefer the for-each form over the values Iterable where possible.","For peek-ahead, keep a 'pending' variable and refill it from hasNext()/next() pairs instead of calling next() speculatively."],"exampleFix":"// before\nwhile (values.hasNext()) {\n  VALUE v = values.next();\n  // ...\n  VALUE peek = values.next(); // may throw past the last value\n}\n\n// after\nwhile (values.hasNext()) {\n  VALUE v = values.next();\n  // ... no speculative next(); use hasNext() before any extra read\n}","handlingStrategy":"validation","validationCode":"if (values.hasNext()) {\n  VALUE v = values.next();\n} else {\n  // no more values for this key\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pair every next() with a fresh hasNext() check.","Prefer for-each over the values Iterable in reduce().","For lookahead, buffer one value yourself instead of calling next() speculatively."],"tags":["hadoop","mapreduce","iterator","reduce","nosuchelement"],"backgroundTag":"iterator-past-end","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}