karatelabs/karate · error · java.util.NoSuchElementException

NoSuchElementException

Error message

NoSuchElementException

What it means

The Map.prototype entries/keys/values iterator throws NoSuchElementException when next() is called on an exhausted map iterator (cursor >= number of entries). In correct JS, next() on a done iterator returns {done:true}; this Java exception means a caller — typically engine-internal — advanced past the end without checking.

Solutions

  1. Check it.next()/result.done before reading value in JS; in Java guard with hasNext().
  2. Snapshot map entries (Array.from(map)) before iterating if the map is mutated during iteration.
  3. Use a fresh iterator per loop; never resume a partially consumed one.
  4. Upgrade karate-js if it fires from library-internal iteration.

Example fix

// before
while (true) { var e = it.next(); if (e.done) break; ... }
// after
var e; while (!(e = it.next()).done) { ... } // single drain, no over-advance
Defensive patterns

Strategy: type-guard

Validate before calling

if (!(m instanceof Map) || m.size === 0) return; // nothing to iterate

Type guard

function nextSafe(it) { const r = it.next(); return r.done ? null : r.value; }

Try / catch

try { let r; while (!(r = it.next()).done) { handle(r.value); } } catch (e) { if (isNoSuchElement(e)) { /* iterator already drained */ } else throw e; }

Prevention

When it happens

Trigger: Calling next() on a Map iterator after every entry has been yielded; mutating the map mid-iteration (add/delete) so the cursor/size accounting desyncs; consuming one iterator from multiple places.

Common situations: JS code like `for (const [k,v] of m)` where the callback throws and a retry re-drains the same iterator; host Java code draining the iterator manually; test262-style tiny-map edge cases with concurrent mutation.

Related errors


AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12). Data as JSON: /api/errors/01f17e63d2242f21. Report an issue: GitHub.

Appendix: source

Thrown at karate-js/src/main/java/io/karatelabs/js/JsMapPrototype.java:206

    /**
     * Iterator that walks the map's entries in insertion order. Per spec, mutations
     * during iteration are observed (entries added after the cursor are visited;
     * deleted entries are skipped). Implemented by snapshotting the keys lazily at
     * each {@code next()} via a fresh entry iterator.
     */
    private static JsIterator mapIterator(JsMap m, MapIteratorKind kind) {
        return new JsIterator() {
            int cursor = 0;

            @Override
            public boolean hasNext() {
                return cursor < m.entries.size();
            }

            @Override
            public Object next() {
                if (cursor >= m.entries.size()) {
                    throw new NoSuchElementException();
                }
                // LinkedHashMap preserves insertion order; advance cursor in step with the
                // sequential view. This is O(n) per step in the worst case but correct under
                // mid-iteration mutation; the test262 suite exercises tiny maps.
                Iterator<Map.Entry<Object, Object>> it = m.entries.entrySet().iterator();
                for (int i = 0; i < cursor; i++) it.next();
                Map.Entry<Object, Object> e = it.next();
                cursor++;
                return switch (kind) {
                    case KEY -> e.getKey();
                    case VALUE -> e.getValue();
                    case KEY_VALUE -> {
                        List<Object> pair = new ArrayList<>(2);
                        pair.add(e.getKey());
                        pair.add(e.getValue());
                        yield pair;
                    }
                };

View on GitHub (pinned to a22eb90246)