{"record":{"id":"081ebdf829ddaff4","repo":"karatelabs/karate","slug":"describe-source-is-not-iterable","errorCode":null,"errorMessage":"${describe(source)} is not iterable","messagePattern":"(.+?) is not iterable","errorType":"exception","errorClass":"JsErrorException","httpStatus":null,"severity":"error","filePath":"karate-js/src/main/java/io/karatelabs/js/IterUtils.java","lineNumber":77,"sourceCode":"     * detached from an iterable receiver.\n     */\n    public static final JsCallable SYMBOL_ITERATOR_METHOD = (ctx, args) -> {\n        Object thisObj = ctx.getThisObject();\n        JsIterator iter = getIterator(thisObj, ctx);\n        return toIteratorObject(iter);\n    };\n\n    private IterUtils() {\n    }\n\n    /**\n     * GetIterator(source) per spec 7.4.1. Throws {@link JsErrorException}\n     * (TypeError) if {@code source} is null/undefined or otherwise not iterable.\n     */\n    public static JsIterator getIterator(Object source, Context context) {\n        JsIterator iter = tryGetIterator(source, context);\n        if (iter == null) {\n            throw JsErrorException.typeError(describe(source) + \" is not iterable\");\n        }\n        return iter;\n    }\n\n    /**\n     * Like {@link #getIterator} but returns null instead of throwing — for callers\n     * that need to gate behavior on iterability without forcing a TypeError.\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static JsIterator tryGetIterator(Object source, Context context) {\n        if (source == null || source == Terms.UNDEFINED) {\n            return null;\n        }\n        if (source instanceof JsArray jsArray) {\n            // The dense fast path is only valid while the array still resolves\n            // the untampered built-in @@iterator. A deleted or replaced\n            // Array.prototype[@@iterator] (or an own-property override) must be\n            // honored — TypeError on deletion, the user's method on override.","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-js/src/main/java/io/karatelabs/js/IterUtils.java#L59-L95","documentation":"IterUtils.getIterator implements GetIterator(source) per the ECMAScript spec (7.4.1). When the value passed to a for-of loop, spread, Array.from, or destructuring is null, undefined, or has no callable Symbol.iterator, tryGetIterator returns null and this TypeError is thrown. It exists to give JS scripts the same TypeError the spec mandates instead of a silent failure or Java NPE.","triggerScenarios":"Calling getIterator (directly or via for-of/spread/Array.from/destructuring) with null or undefined, or with an object that lacks a callable Symbol.iterator property, e.g. `for (var x of null)`, `[...5]`, or spreading a plain non-iterable object.","commonSituations":"A JSON path or API response returned null/undefined instead of an array (empty result set, missing field in response), a function that should return an array returned a scalar, or a Java object was passed into JS and karate did not auto-wrap it as iterable.","solutions":["Guard the value before iterating: `if (response != undefined && response.length !== undefined)` or default it `var list = response || []`.","Fix the data source so the API/JSON path returns an array instead of null.","If iterating a custom object, give it a Symbol.iterator method returning an iterator object with a callable next().","Catch the TypeError in JS (try/catch) if absence of data is an expected case."],"exampleFix":"// before\nfor (var item of response.items) { ... }\n// after\nvar items = response.items == null ? [] : response.items;\nfor (var item of items) { ... }","handlingStrategy":"validation","validationCode":"if (source == null || typeof source[Symbol.iterator] !== 'function') {\n  throw new TypeError('value is not iterable: ' + String(source));\n}","typeGuard":"function isIterable(v) { return v != null && typeof v[Symbol.iterator] === 'function'; }","tryCatchPattern":"try { for (var x of source) { ... } } catch (e) { if (String(e).indexOf('not iterable') !== -1) { source = []; } else { throw e; } }","preventionTips":["Default nullable response fields to [] before looping","Assert API response shape (karate.match) before iterating","Only spread/array-spread values you know are arrays or strings"],"tags":["javascript","iteration","typeerror"],"backgroundTag":"incompatible-source-type","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"}