karatelabs/karate · error · java.util.NoSuchElementException

NoSuchElementException

Error message

NoSuchElementException

What it means

JsGlobalThis's property iterator (used to enumerate the global object's bindings) throws NoSuchElementException when next() is called after the last entry. Like the other JS iterators, callers must consult hasNext() first; the exception signals iteration past the end of the global-object entry set.

Solutions

  1. Always pair next() with hasNext() (or a for-each loop).
  2. Get a fresh iterator for each enumeration pass.
  3. Avoid mutating the global object while enumerating it; collect keys first, then mutate.
  4. If thrown from library internals during global enumeration, report with a repro; upgrade karate-js.

Example fix

// before
KeyValue kv = it.next(); // assumes another entry exists
// after
if (it.hasNext()) { KeyValue kv = it.next(); }
Defensive patterns

Strategy: type-guard

Type guard

boolean canAdvance(Iterator<?> it) { return it != null && it.hasNext(); }

Try / catch

try { KeyValue kv = it.next(); } catch (NoSuchElementException e) { /* exhausted: treat as end of enumeration */ }

Prevention

When it happens

Trigger: Advancing the globalThis entry iterator past its final key; internal enumeration loops (for...in / Object.keys style) driven by buggy or duplicated consumption of the same iterator.

Common situations: Host Java code enumerating global variables; engine bugs when globals are added/removed mid-enumeration; iterating after a prior full pass without resetting.

Related errors


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

Appendix: source

Thrown at karate-js/src/main/java/io/karatelabs/js/JsGlobalThis.java:243

                    if (bindings().isTombstoned(e.getKey())) continue;
                    if (isEnumerable(e.getKey())) {
                        peeked = e;
                        return true;
                    }
                }
                peeked = null;
                return false;
            }

            @Override
            public boolean hasNext() {
                return peeked != null || advance();
            }

            @Override
            public KeyValue next() {
                if (peeked == null && !advance()) {
                    throw new NoSuchElementException();
                }
                Map.Entry<String, Object> entry = peeked;
                peeked = null;
                return new KeyValue(JsGlobalThis.this, index++, entry.getKey(), entry.getValue());
            }
        };
    }

    @Override
    public Map<String, Object> toMap() {
        // Raw view of the unified store — both visible and hidden entries,
        // identity preserved (no JsFunction → JsFunctionWrapper). Built-ins
        // not yet lazy-cached aren't here; Object.keys(globalThis) only sees
        // realized entries (and gets filtered down further by enumerability
        // via getOwnAttrs above). Merge in any JsObject.props entries — those
        // hold accessor descriptors installed via Object.defineProperty(this, …)
        // (BindingSlot is data-only).
        Map<String, Object> raw = bindings().getRawMap();

View on GitHub (pinned to a22eb90246)