oracle/graal · error · NoSuchElementException

Empty cursor does not have elements

Error message

Empty cursor does not have elements

What it means

MapCursor.remove() on the shared EMPTY_CURSOR singleton (returned by EconomicMap.getEntries() for an empty map) always throws NoSuchElementException. An empty cursor is positioned on no element, so there is nothing to remove; the library treats calling a cursor method that requires a current element as an iteration error, mirroring java.util.Iterator semantics.

Source

Thrown at sdk/src/org.graalvm.collections/src/org/graalvm/collections/EmptyMap.java:55

 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package org.graalvm.collections;

import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.function.BiFunction;

/**
 * Singleton instances for empty maps and the corresponding iterators and cursors.
 */
class EmptyMap {

    static final MapCursor<Object, Object> EMPTY_CURSOR = new MapCursor<>() {
        @Override
        public void remove() {
            throw new NoSuchElementException("Empty cursor does not have elements");
        }

        @Override
        public boolean advance() {
            return false;
        }

        @Override
        public Object getKey() {
            throw new NoSuchElementException("Empty cursor does not have elements");
        }

        @Override
        public Object getValue() {
            throw new NoSuchElementException("Empty cursor does not have elements");
        }

        @Override

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Only call cursor.remove() after cursor.advance() has returned true, exactly once per element.
  2. Check map.isEmpty() (or !cursor.advance()) before performing cursor operations.
  3. Prefer map.removeKey(key) over cursor-based removal when you already know the key.

Example fix

// before
MapCursor<K,V> c = map.getEntries();
while (c.advance()) { ... }
c.remove(); // wrong: cursor exhausted / empty

// after
MapCursor<K,V> c = map.getEntries();
while (c.advance()) {
    if (shouldDrop(c.getKey())) {
        c.remove(); // valid: current element exists
    }
}
Defensive patterns

Strategy: validation

Validate before calling

if (!map.isEmpty()) {
    MapCursor<K,V> c = map.getEntries();
    while (c.advance()) {
        if (drop(c.getKey())) c.remove(); // remove only while positioned
    }
}

Prevention

When it happens

Trigger: Obtaining a cursor from an empty EconomicMap via map.getEntries() and calling remove() without a preceding successful advance(). Note the cursor starts 'before' the first element; remove() is only valid after advance() returned true. Also triggered by calling remove() twice after one advance().

Common situations: Generic iteration code written for java.util.Map.entrySet().iterator() ported to MapCursor; cleanup loops that call remove() unconditionally per iteration; cursors obtained from EconomicMaps.createMap() that is later emptied by clear() before iteration.

Related errors


AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14). Data as JSON: /api/errors/18145310a8137643. Report an issue: GitHub.