karatelabs/karate · error · JsErrorException

iterator result is not an object

Error message

iterator result is not an object

What it means

After calling `next()` on a user-supplied iterator object, karate requires the returned step to be an object (per spec IteratorStep, which does ToObject on the result). If next() returns a primitive, null, or undefined, this TypeError is thrown.

Solutions

  1. Always return a result object from next(): `return { value: v, done: false }`.
  2. Wrap primitive returns: `return { value: compute(), done: false };`.
  3. Ensure the terminal call also returns an object: `return { done: true };`.

Example fix

// before
next: function() { return items[i++]; }
// after
next: function() { return i < items.length ? { value: items[i++], done: false } : { done: true }; }
Defensive patterns

Strategy: type-guard

Validate before calling

var step = it.next();
if (step == null || typeof step !== 'object') throw new TypeError('next() must return an object');

Type guard

function isIteratorStep(s) { return s != null && typeof s === 'object' && typeof s.done === 'boolean'; }

Try / catch

try { var step = it.next(); consume(step.value); } catch (e) { if (String(e).indexOf('not an object') !== -1) { /* treat iterator as broken */ } else { throw e; } }

Prevention

When it happens

Trigger: A custom iterator's next() function returns a bare value like `return i++` or `return null` instead of `{value: x, done: bool}`, and the iterator is consumed by for-of, spread, or Array.from.

Common situations: Beginner iterator implementations returning values directly, refactored next() that dropped the result-object wrapper, or next() delegating to a function that returns a primitive.

Related errors


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

Appendix: source

Thrown at karate-js/src/main/java/io/karatelabs/js/IterUtils.java:220

                Object nextFn = iterObj.getMember("next");
                if (!(nextFn instanceof JsCallable nextCallable)) {
                    throw JsErrorException.typeError("iterator.next is not a function");
                }
                Object step;
                if (context instanceof CoreContext cc) {
                    Object savedThis = cc.thisObject;
                    cc.thisObject = iterObj;
                    try {
                        step = nextCallable.call(cc, EMPTY_ARGS);
                    } finally {
                        cc.thisObject = savedThis;
                    }
                } else {
                    step = nextCallable.call(context, EMPTY_ARGS);
                }
                if (isJsErrored(context)) { done = true; return; }
                if (!(step instanceof ObjectLike stepObj)) {
                    throw JsErrorException.typeError("iterator result is not an object");
                }
                if (Terms.isTruthy(readMember(stepObj, "done", context))) {
                    done = true;
                    return;
                }
                if (isJsErrored(context)) { done = true; return; }
                pending = readMember(stepObj, "value", context);
                if (pending == null && !hasProperty(stepObj, "value")) {
                    // A step result with no `value` member reads as undefined
                    // (spec GetV) — the distinction matters downstream, where
                    // a destructuring default fires on undefined but not null.
                    pending = Terms.UNDEFINED;
                }
                if (isJsErrored(context)) { done = true; return; }
                fetched = true;
            }

            @Override

View on GitHub (pinned to a22eb90246)