karatelabs/karate · error · JsErrorException
is not iterable
Error message
${describe(source)} is not iterable What it means
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.
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.
Example fix
// before
for (var item of response.items) { ... }
// after
var items = response.items == null ? [] : response.items;
for (var item of items) { ... } Defensive patterns
Strategy: validation
Validate before calling
if (source == null || typeof source[Symbol.iterator] !== 'function') {
throw new TypeError('value is not iterable: ' + String(source));
} Type guard
function isIterable(v) { return v != null && typeof v[Symbol.iterator] === 'function'; } Try / catch
try { for (var x of source) { ... } } catch (e) { if (String(e).indexOf('not iterable') !== -1) { source = []; } else { throw e; } } Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- Cannot set property ' ' which has only a getter
- assignment to constant
- assignment to constant
- Generator is already running
- groupBy called with null or undefined items
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/081ebdf829ddaff4.
Report an issue: GitHub.
Appendix: source
Thrown at karate-js/src/main/java/io/karatelabs/js/IterUtils.java:77
* detached from an iterable receiver.
*/
public static final JsCallable SYMBOL_ITERATOR_METHOD = (ctx, args) -> {
Object thisObj = ctx.getThisObject();
JsIterator iter = getIterator(thisObj, ctx);
return toIteratorObject(iter);
};
private IterUtils() {
}
/**
* GetIterator(source) per spec 7.4.1. Throws {@link JsErrorException}
* (TypeError) if {@code source} is null/undefined or otherwise not iterable.
*/
public static JsIterator getIterator(Object source, Context context) {
JsIterator iter = tryGetIterator(source, context);
if (iter == null) {
throw JsErrorException.typeError(describe(source) + " is not iterable");
}
return iter;
}
/**
* Like {@link #getIterator} but returns null instead of throwing — for callers
* that need to gate behavior on iterability without forcing a TypeError.
*/
@SuppressWarnings("unchecked")
public static JsIterator tryGetIterator(Object source, Context context) {
if (source == null || source == Terms.UNDEFINED) {
return null;
}
if (source instanceof JsArray jsArray) {
// The dense fast path is only valid while the array still resolves
// the untampered built-in @@iterator. A deleted or replaced
// Array.prototype[@@iterator] (or an own-property override) must be
// honored — TypeError on deletion, the user's method on override.View on GitHub (pinned to a22eb90246)