{"record":{"id":"f07a371c46dbed8a","repo":"ruby-concurrency/concurrent-ruby","slug":"illegalstateexception","errorCode":null,"errorMessage":"IllegalStateException","messagePattern":"IllegalStateException","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java","lineNumber":2501,"sourceCode":"                        if ((ek = e.key) instanceof TreeBin)\n                            e = ((TreeBin)ek).first;\n                        else {\n                            tab = (Node[])ek;\n                            continue;           // restarts due to null val\n                        }\n                    }                           // visit upper slots if present\n                    index = (i += baseSize) < n ? i : (baseIndex = b + 1);\n                }\n                nextKey = (K) e.key;\n            } while ((ev = (V) e.val) == null);    // skip deleted or special nodes\n            next = e;\n            return nextVal = ev;\n        }\n\n        public final void remove() {\n            Object k = nextKey;\n            if (k == null && (advance() == null || (k = nextKey) == null))\n                throw new IllegalStateException();\n            map.internalReplace(k, null, null);\n        }\n\n        public final boolean hasNext() {\n            return nextVal != null || advance() != null;\n        }\n\n        public final boolean hasMoreElements() { return hasNext(); }\n        public final void setRawResult(Object x) { }\n        public R getRawResult() { return null; }\n        public boolean exec() { return true; }\n    }\n\n    /* ---------------- Public operations -------------- */\n\n    /**\n     * Creates a new, empty map with the default initial table size (16).\n     */","sourceCodeStart":2483,"sourceCodeEnd":2519,"githubUrl":"https://github.com/ruby-concurrency/concurrent-ruby/blob/0b88d5ff75f69b3740c8f0868e76f833cb2fd45d/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java#L2483-L2519","documentation":"Every iterator and enumeration from keySet(), values(), entrySet(), keys() and elements() is backed by the internal Traverser class. Its remove() needs a key latched by a previous advance/next; if nextKey is null and advance() can find no element, there is nothing to remove and it throws IllegalStateException. Note this implementation deviates from java.util iterators: remove() before the first next() on a NON-empty map silently deletes an element instead of throwing, and a double remove can quietly no-op rather than throw.","triggerScenarios":"Calling it.remove() before the first next() when the map is empty; calling it.remove() after traversal finished and concurrent removals emptied the map; a filtering loop where the it.next() call is conditional but it.remove() is unconditional, so remove() eventually runs with no latched element.","commonSituations":"Hand-rolled filter loops that break the next-before-remove pairing required by all JDK iterators; racing threads draining the map while another thread iterates; porting code between for-each and while-hasNext styles and losing the next() call.","solutions":["Call it.remove() only immediately after a successful it.next() returned the element you want deleted","Track removal eligibility with a boolean set by next() and cleared after remove()","Prefer collecting keys during iteration and calling map.remove(key) after the loop instead of iterator remove()","Restructure the loop so next() always executes before any remove() can run"],"exampleFix":"// before\nIterator<String> it = map.keySet().iterator();\nif (shouldDropFirst) it.remove(); // IllegalStateException on empty map\n\n// after\nIterator<String> it = map.keySet().iterator();\nwhile (it.hasNext()) {\n    String k = it.next();\n    if (shouldDrop(k)) it.remove();\n}","handlingStrategy":"validation","validationCode":"Iterator<K> it = map.keySet().iterator();\nwhile (it.hasNext()) {\n    K k = it.next();   // always latch an element first\n    if (shouldDrop(k)) it.remove();\n}","typeGuard":null,"tryCatchPattern":"try { it.remove(); } catch (IllegalStateException e) { // no element latched: iterator exhausted or map empty - fix the loop structure, do not ignore }","preventionTips":["Always pair remove() with a preceding next() in the same iteration","Prefer map.remove(key) loops over iterator.remove under concurrency","Do not assume a second remove throws here - it can silently no-op"],"tags":["iterator","illegal-state","traverser","concurrent-map","remove"],"backgroundTag":"iterator-remove-without-next","analyzedSha":"0b88d5ff75f69b3740c8f0868e76f833cb2fd45d","analyzedAt":"2026-08-21T20:12:56.291Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}