karatelabs/karate · error · JsErrorException

groupBy called with null or undefined items

Error message

groupBy called with null or undefined items

What it means

GroupByImpl.run throws this TypeError when the items argument to groupBy (Object.groupBy / Map.groupBy style API, also property-mode) is null or undefined. There is nothing to iterate, so instead of a confusing downstream iterator failure the engine fails fast with an explicit message.

Solutions

  1. Ensure the items argument is an array or iterable before calling groupBy (default it to [])
  2. Null-check the source expression (karate: `karate.isNull(x)`, JS: `x ?? []`)
  3. Fix the upstream step that produced null/undefined (missing response field, wrong path)
  4. If null is meaningful, branch around the groupBy call instead of passing null

Example fix

// before
const groups = groupBy(response.items, x => x.type);
// after
const groups = groupBy(response.items ?? [], x => x.type);
Defensive patterns

Strategy: validation

Validate before calling

if (items == null) throw new Error('groupBy: items required');
// or in JS: if (items == null) items = [];

Type guard

function isIterableItems(x) { return x != null && typeof x[Symbol.iterator] === 'function'; }

Try / catch

try { groupBy(items, cb); } catch (e) { if (String(e).includes('null or undefined')) { items = []; } else { throw e; } }

Prevention

When it happens

Trigger: Calling the groupBy helper with a variable that is null/undefined — e.g. an unset karate variable, a function that returned undefined, or a missing response field passed as the collection.

Common situations: Chained operations where an earlier step returned nothing (`response.foo` when foo is absent); optional JSON paths that yielded null; config values not yet set before the groupBy call.

Related errors


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

Appendix: source

Thrown at karate-js/src/main/java/io/karatelabs/js/GroupByImpl.java:65

        final List<Object> values = new ArrayList<>();

        Group(Object key) {
            this.key = key;
        }
    }

    /**
     * Walks {@code items} via the spec iterator protocol, invokes
     * {@code callback(value, k)} for each element, coerces the return value
     * into a key per {@code propertyMode} (true → ToPropertyKey, false →
     * -0 → +0 normalize), and accumulates values into groups in
     * first-seen-key order. Group key equality uses {@link Terms#sameValue}
     * (matches spec for the Map.groupBy case; for property-mode the keys are
     * always strings and SameValue collapses to equals).
     */
    static List<Group> run(Object items, Object callback, boolean propertyMode, Context context) {
        if (items == null || items == Terms.UNDEFINED) {
            throw JsErrorException.typeError("groupBy called with null or undefined items");
        }
        if (!(callback instanceof JsCallable cb)) {
            throw JsErrorException.typeError("groupBy callback is not a function");
        }
        CoreContext cc = context instanceof CoreContext c ? c : null;
        JsIterator iter = IterUtils.getIterator(items, context);
        List<Group> groups = new ArrayList<>();
        long k = 0;
        while (iter.hasNext()) {
            Object value = iter.next();
            Object rawKey = cb.call(context, new Object[]{value, (double) k});
            // IfAbruptCloseIterator: a callback that threw via context.error
            // ends iteration without re-grouping. Surface the same error to
            // our caller (already on cc.error).
            if (cc != null && cc.isError()) {
                return groups;
            }
            Object key;

View on GitHub (pinned to a22eb90246)