{"record":{"id":"4ff8840c7ffc392b","repo":"stanfordnlp/CoreNLP","slug":"call-next-before-calling-remove","errorCode":null,"errorMessage":"Call next() before calling remove()!","messagePattern":"Call next\\(\\) before calling remove\\(\\)!","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"src/edu/stanford/nlp/util/CollectionUtils.java","lineNumber":932,"sourceCode":"      public boolean hasNext() {\n        return !iters.isEmpty() && iters.get(0).hasNext();\n      }\n      @Override\n      public E next() {\n        if (!hasNext()) {\n          throw new IllegalArgumentException(\"Iterator is empty!\");\n        }\n        E next = iters.get(0).next();\n        lastIter = iters.get(0);\n        while (!iters.isEmpty() && !iters.get(0).hasNext()) {\n          iters.remove(0);\n        }\n        return next;\n      }\n      @Override\n      public void remove() {\n        if (lastIter == null) {\n          throw new IllegalStateException(\"Call next() before calling remove()!\");\n        }\n        lastIter.remove();\n      }\n    };\n  }\n\n  public static <E> Iterator<E> iteratorFromEnumerator(final Enumeration<E> lst_) {\n    return new Iterator<E>() {\n      private final Enumeration<E> lst = lst_;\n      @Override\n      public boolean hasNext() {\n        return lst.hasMoreElements();\n      }\n\n      @Override\n      public E next() {\n        return lst.nextElement();\n      }","sourceCodeStart":914,"sourceCodeEnd":950,"githubUrl":"https://github.com/stanfordnlp/CoreNLP/blob/1b7edd19c4d0d7b1f13a2591425b9b60a0b1af7a/src/edu/stanford/nlp/util/CollectionUtils.java#L914-L950","documentation":"The concatenated iterator's remove() throws IllegalStateException when called before any next() on the current underlying iterator (lastIter is still null). Per the Iterator contract, remove() must follow a next(); this class enforces that explicitly.","triggerScenarios":"Calling remove() as the first operation on a freshly created concatenated iterator, or calling remove() twice in a row without an intervening next().","commonSituations":"Filtering loops that call remove() before the first element is read; retry/removal logic that lost track of the last next() call.","solutions":["Only call remove() immediately after a successful next() call.","Track last-read state yourself; never call remove() twice per next().","Collect elements to remove during iteration and remove them from the source after the loop, avoiding iterator remove() entirely.","Use a for-each loop with post-loop source mutation when mutation is needed."],"exampleFix":"// before\nIterator<E> it = concat.iterator();\nit.remove(); // IllegalStateException\n// after\nIterator<E> it = concat.iterator();\nif (it.hasNext()) { E e = it.next(); it.remove(); }","handlingStrategy":"type-guard","validationCode":"if (!it.hasNext() && !hasCalledNext) { /* do not call remove() */ }","typeGuard":"boolean canRemove(Iterator<?> it, boolean lastCallWasNext) { return lastCallWasNext; }","tryCatchPattern":"try { it.remove(); } catch (IllegalStateException e) { /* no prior next(); skip removal */ }","preventionTips":["Call remove() only immediately after next()","Never call remove() twice per next()","Prefer post-loop removeAll() on the source collection","Document mutation contracts when wrapping iterators"],"tags":["java","collections","iterator","illegal-state"],"backgroundTag":"invalid-state-transition","analyzedSha":"1b7edd19c4d0d7b1f13a2591425b9b60a0b1af7a","analyzedAt":"2026-09-10T02:24:07.274Z","contentChangedAt":"2026-09-10T02:24:07.274Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}