karatelabs/karate · error · TypeError

Set.prototype.forEach: callback is not a function

Error message

Set.prototype.forEach: callback is not a function

What it means

Set.prototype.forEach requires its first argument to be a callable callback function; a non-function (including undefined) was supplied. This mirrors the spec TypeError for non-function iterables' callbacks.

Solutions

  1. Pass a function: mySet.forEach(v => console.log(v)).
  2. Default optional callbacks: cb = cb || (() => {}).
  3. Validate typeof callback === 'function' before calling forEach.

Example fix

// before
mySet.forEach(callbackName);
// after
if (typeof callbackName === 'function') { mySet.forEach(callbackName); }
Defensive patterns

Strategy: validation

Validate before calling

if (typeof cb !== 'function') cb = () => {};

Type guard

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

Try / catch

try { mySet.forEach(cb); } catch (e) { if (String(e).includes('callback is not a function')) mySet.forEach(v => process(v)); else throw e; }

Prevention

When it happens

Trigger: mySet.forEach() with no args; mySet.forEach('notAFunction'); passing a value that is null/undefined because a callback variable was never initialized.

Common situations: Typos where the intended callback name is misspelled or out of scope; optional callbacks defaulted to undefined instead of a no-op; refactors that dropped the arrow function.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at karate-js/src/main/java/io/karatelabs/js/JsSetPrototype.java:98

    }

    private Object has(Context context, Object[] args) {
        return asSet(context).has(args.length > 0 ? args[0] : Terms.UNDEFINED);
    }

    private Object delete(Context context, Object[] args) {
        return asSet(context).deleteValue(args.length > 0 ? args[0] : Terms.UNDEFINED);
    }

    private Object clear(Context context, Object[] args) {
        asSet(context).clearAll();
        return Terms.UNDEFINED;
    }

    private Object forEach(Context context, Object[] args) {
        JsSet s = asSet(context);
        if (args.length == 0 || !(args[0] instanceof JsCallable cb)) {
            throw JsErrorException.typeError("Set.prototype.forEach: callback is not a function");
        }
        Object thisArg = args.length > 1 ? args[1] : Terms.UNDEFINED;
        CoreContext cc = context instanceof CoreContext c ? c : null;
        Object savedThis = cc != null ? cc.thisObject : null;
        try {
            // Spec: forEach walks the live entry list. Entries added after the cursor
            // (at the end) are visited; entries deleted before the cursor reaches them
            // are skipped. Re-fetch the keyset on each step so additions show up.
            int cursor = 0;
            while (cursor < s.elements.size()) {
                Iterator<Object> it = s.elements.keySet().iterator();
                for (int i = 0; i < cursor && it.hasNext(); i++) it.next();
                if (!it.hasNext()) break;
                Object v = it.next();
                if (cc != null) cc.thisObject = thisArg;
                // Per spec: callback receives (value, value, set) — both first args identical
                // (sets have no separate key vs value).
                cb.call(context, new Object[]{v, v, s});

View on GitHub (pinned to a22eb90246)