{"record":{"id":"342c41fe1d4dfbaf","repo":"karatelabs/karate","slug":"result-of-the-symbol-iterator-method-is-not-an-object","errorCode":null,"errorMessage":"Result of the Symbol.iterator method is not an object","messagePattern":"Result of the Symbol\\.iterator method is not an object","errorType":"exception","errorClass":"JsErrorException","httpStatus":null,"severity":"error","filePath":"karate-js/src/main/java/io/karatelabs/js/Interpreter.java","lineNumber":2940,"sourceCode":"     * over the raw iterator record (iterator object + its methods invoked\n     * with arguments), NOT the value-only JsIterator walk: sent values,\n     * throw/return forwarding, and the delegate's final completion value all\n     * survive. Each re-yield goes through the same activation handoff, so\n     * the next resume kind comes from whatever the driver sends.\n     */\n    private static Object evalYieldStar(Object operand, CoreContext context, GeneratorActivation act) {\n        // GetIterator: call operand[@@iterator]() with operand as `this`\n        JsIterator probe = null; // only to reuse getIterator's TypeError wording on non-iterables\n        Object iteratorFn = operand instanceof ObjectLike ol\n                ? ol.getMember(IterUtils.SYMBOL_ITERATOR, ol, context) : null;\n        ObjectLike iterObj;\n        if (iteratorFn instanceof JsCallable callable && operand instanceof ObjectLike receiver) {\n            Object iter = callWith(callable, receiver, context);\n            if (context.isStopped()) {\n                return Terms.UNDEFINED;\n            }\n            if (!(iter instanceof ObjectLike io)) {\n                throw JsErrorException.typeError(\"Result of the Symbol.iterator method is not an object\");\n            }\n            iterObj = io;\n        } else {\n            // strings / raw lists / Java iterables — wrap the engine iterator\n            // in the standard iterator-object shape so one loop serves all\n            probe = IterUtils.getIterator(operand, context);\n            iterObj = IterUtils.toIteratorObject(probe);\n        }\n        GeneratorActivation.ResumeKind kind = GeneratorActivation.ResumeKind.NEXT;\n        Object sent = Terms.UNDEFINED;\n        while (true) {\n            Object step;\n            switch (kind) {\n                case NEXT -> {\n                    step = invokeIteratorMethod(iterObj, \"next\", sent, context, true);\n                    if (context.isStopped()) {\n                        return Terms.UNDEFINED;\n                    }","sourceCodeStart":2922,"sourceCodeEnd":2958,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-js/src/main/java/io/karatelabs/js/Interpreter.java#L2922-L2958","documentation":"Karate's JS engine evaluates spread/destructuring/for-of by calling the operand's Symbol.iterator method; the spec requires that call to return an object. The engine throws this TypeError when Symbol.iterator exists and is callable but returns a non-object (e.g. undefined, a number, or a primitive). It enforces ECMAScript GetIterator semantics on custom iterator implementations.","triggerScenarios":"A user-defined JS object defines a callable Symbol.iterator whose body returns undefined, a primitive, or forgets a return statement, and the object is then spread (...obj), destructured, or used in for-of.","commonSituations":"Hand-written iterator classes where the factory function returns nothing; iterators written for another engine that rely on implicit returns; refactoring that turned `return { next() {...} }` into just building the object without returning it.","solutions":["Make Symbol.iterator return an object — typically `{ next() { ... } }` or a generator function result","Convert the method to a generator (`*[Symbol.iterator]() { ... }`) so the engine returns the iterator object automatically","If the value may not be iterable, guard with a check before spreading or looping"],"exampleFix":"// before\nconst obj = { *[Symbol.iterator]() {} }; // fine, but:\nconst bad = { [Symbol.iterator]() { const it = makeIter(); } }; // no return\n// after\nconst good = { [Symbol.iterator]() { return makeIter(); } };","handlingStrategy":"validation","validationCode":"// JS\nfunction isIterable(x) { return x != null && typeof x[Symbol.iterator] === 'function'; }\nif (!isIterable(obj)) throw new TypeError('not iterable');","typeGuard":"function isIterableWithIterator(x) {\n  const it = x != null && typeof x[Symbol.iterator] === 'function' ? x[Symbol.iterator]() : null;\n  return it !== null && typeof it === 'object';\n}","tryCatchPattern":"try { for (const v of obj) { /* ... */ } } catch (e) { if (String(e).includes('Symbol.iterator')) { /* fall back to non-iterable handling */ } }","preventionTips":["Prefer generator methods (*[Symbol.iterator]()) over hand-built iterator factories","Always return an object from Symbol.iterator","Test spread/destructuring of custom collections in unit tests"],"tags":["javascript","iterator","typeerror"],"backgroundTag":"type-mismatch","analyzedSha":"a22eb90246d958d15a47bf436693d0121ad2812d","analyzedAt":"2026-09-12T09:01:00.220Z","contentChangedAt":"2026-09-12T09:01:00.220Z","schemaVersion":2},"datasetVersion":"2026-09-16T19:17:19.609Z"}