karatelabs/karate · error · JsErrorException

callback is not a function

Error message

 callback is not a function

What it means

Array iteration/reduce/sort methods require their first argument to be a callable callback per spec. Karate's requireCallable throws TypeError '<methodName> callback is not a function' when args[0] is missing or not a JsCallable, keeping messages like 'Array.prototype.map called on non-callable' spec-shaped.

Solutions

  1. Pass an actual function: arr.map(x => x * 2)
  2. Check the variable holds a function before the call: typeof cb === 'function'
  3. Fix typos or failed imports that left the callback undefined
  4. Use a no-op or identity function when a callback is genuinely unneeded

Example fix

// before
arr.map(callback); // callback is undefined
// after
if (typeof callback !== 'function') callback = x => x;
arr.map(callback);
Defensive patterns

Strategy: type-guard

Validate before calling

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

Type guard

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

Try / catch

try { return arr.map(cb); } catch (e) { if (String(e.message).includes('callback is not a function')) return arr; throw e; }

Prevention

When it happens

Trigger: arr.map() with no arguments; arr.filter('string'); arr.sort(42); passing a non-function (null, object, number) as the callback to map/filter/every/some/forEach/find/reduce/sort etc.

Common situations: Typo'd callback names (arr.map(calback)); variables holding non-functions because a lookup failed; calling methods with stale signatures after refactors; confusing sort (comparator) and map (callback).

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/932672d76bf02248. Report an issue: GitHub.

Appendix: source

Thrown at karate-js/src/main/java/io/karatelabs/js/JsArrayPrototype.java:476

            throw JsErrorException.typeError("Array.prototype.* called on null or undefined");
        }
        return o;
    }

    private static final Object[] EMPTY_ARGS = new Object[0];

    /** Spec {@code IsCallable} guard — every {@code Array.prototype.*}
     *  iteration method begins with a TypeError when the supplied callbackfn
     *  is not a function (spec FindViaPredicate / map / filter / forEach /
     *  every / some / reduce / reduceRight / flatMap step 1). The method
     *  name flows into the error so test262's
     *  {@code Array.prototype.map called on non-callable} style assertions
     *  carry the spec context. */
    private static JsCallable requireCallable(Object[] args, String methodName) {
        if (args.length > 0 && args[0] instanceof JsCallable callable) {
            return callable;
        }
        throw JsErrorException.typeError(methodName + " callback is not a function");
    }

    /** Spec {@code Call(callbackfn, thisArg, …)} hookup for the iteration
     *  methods. When {@code methodArgs[thisArgIdx]} is supplied, wraps
     *  {@code inner} so each invocation temporarily binds the user-supplied
     *  {@code thisArg} as the call-site's {@code this} via save/restore on
     *  the live {@link CoreContext} — {@code Array.prototype.map.call(arr,
     *  fn, ctx)}'s {@code fn} then sees {@code this === ctx} per §23.1.3.21.
     *  Save/restore (rather than allocating a child context) keeps error
     *  propagation simple — the user function's throw lands on the same
     *  context the iteration loop checks via {@link CoreContext#isError()},
     *  no intermediate frame to {@code updateFrom}.
     *  <p>
     *  When {@code thisArg} is absent (caller passed fewer args), spec wants
     *  {@code Call(fn, undefined, …)}; for sloppy-mode callbacks ToObject
     *  then promotes {@code undefined} to {@code globalThis}. We don't model
     *  the strict / sloppy split, so when {@code thisArg} is absent we
     *  leave the existing {@code this} alone — keeps idiomatic

View on GitHub (pinned to a22eb90246)