karatelabs/karate · error · JsErrorException

groupBy callback is not a function

Error message

groupBy callback is not a function

What it means

GroupByImpl.run throws this TypeError when the callback argument is not a callable (not a JsCallable). groupBy needs a key-selector function (or a property name in property-mode resolved elsewhere), so a non-function callback cannot produce group keys.

Solutions

  1. Pass an actual function: `groupBy(items, item => item.key)`
  2. If you meant property-mode, use the property-mode entry point with the property name string
  3. Check the callback identifier isn't undefined (typo, wrong scope)
  4. Remove `()` if you accidentally invoked the callback instead of passing it

Example fix

// before
groupBy(items, 'typeKey'); // string in function mode
// after
groupBy(items, item => item.typeKey);
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof callback !== 'function') throw new Error('callback must be a function');

Type guard

function isFn(x) { return typeof x === 'function'; }

Try / catch

try { groupBy(items, cb); } catch (e) { if (String(e).includes('not a function')) { /* fix callback arg */ } else { throw e; } }

Prevention

When it happens

Trigger: Passing a non-function as the second argument: a string in function-mode, an undefined variable, the result of calling the function instead of passing it (`cb(x)` vs `cb`), or a non-callable object.

Common situations: Typo where the callback name is undefined; forgetting that property-mode uses a different call shape so a string lands in the function-mode branch; arrow function vs invocation confusion.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

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

            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;
            if (propertyMode) {
                // Spec ToPropertyKey — dispatches JS toString for ObjectLike values.
                key = Terms.toPropertyKey(rawKey, cc);

View on GitHub (pinned to a22eb90246)