karatelabs/karate · error · JsErrorException

is not a constructor

Error message

${node.getTextIncludingWhitespace()} is not a constructor

What it means

invokeCallable throws this TypeError when `new X(...)` is used with a value that, while callable, is not constructable (JsCallable.isConstructable() is false) — mirroring the ECMAScript rule that e.g. arrow functions, methods, and built-ins like Math.* cannot be constructors. The message includes the source text of the callee expression.

Solutions

  1. Remove `new` and call the function normally if it returns the object you need
  2. Convert the arrow function/method to a regular `function` or `class` if construction is intended
  3. If it's a built-in, use its factory API instead of `new` (e.g. Array.isArray vs new-wrapper patterns)
  4. Check the variable actually refers to a constructable class and not an instance or helper

Example fix

// before
const Point = (x, y) => ({ x, y });
const p = new Point(1, 2); // not a constructor
// after
const Point = (x, y) => ({ x, y });
const p = Point(1, 2);
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof C === 'function' && !C.prototype || isArrow(C)) { /* call without new */ }

Type guard

function isConstructable(f) { try { return typeof f === 'function' && /^\s*(class|function)/.test(String(f)); } catch (e) { return false; } }

Try / catch

try { var o = new C(); } catch (e) { if (String(e).includes('is not a constructor')) { o = C(); } else { throw e; } }

Prevention

When it happens

Trigger: `new arrowFn()`, `new someObjectMethod()`, `new (() => 1)`, or `new` on class-less helpers / host-provided functions the engine marks non-constructable (karate built-ins, arrow callbacks defined in scripts).

Common situations: JS code written before the callee was refactored to an arrow function; using `new` by habit on helper functions; copying CommonJS/browser code that wrapped a constructor that karate models as a plain callable.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at karate-js/src/main/java/io/karatelabs/js/Interpreter.java:649

    // Handles `a?.(args)` shape: REF_DOT_EXPR[base, FN_CALL_EXPR[?., (, args, )]].
    // Per spec the chain head is the base (must be evaluated once); if nullish,
    // short-circuit the entire call without evaluating the args. Goes through
    // getCallable so a method-reference base (`a.b?.()`) keeps `a` as receiver.
    private static Object evalOptionalCall(Node node, CoreContext context) {
        Object o = PropertyAccess.getCallable(node.getFirst(), context);
        Object receiver = context.callReceiver; // consume immediately (see field contract)
        if (o == PropertyAccess.SHORT_CIRCUITED) return PropertyAccess.SHORT_CIRCUITED;
        if (o == null || o == Terms.UNDEFINED) return PropertyAccess.SHORT_CIRCUITED;
        Node callExpr = node.get(1); // FN_CALL_EXPR -> [?., (, FN_CALL_ARGS, )]
        Node fnArgsNode = callExpr.get(2);
        return invokeCallable(o, receiver, fnArgsNode, false, node.getFirst(), context);
    }

    private static Object invokeCallable(Object o, Object receiver, Node fnArgsNode,
                                          boolean newKeyword, Node node, CoreContext context) {
        if (o instanceof JsCallable callable) {
            if (newKeyword && !callable.isConstructable()) {
                throw JsErrorException.typeError(node.getTextIncludingWhitespace() + " is not a constructor");
            }
            if (!newKeyword && callable instanceof JsFunctionNode cf && cf.isClassConstructor) {
                String n = cf.name == null || cf.name.isEmpty() ? "" : cf.name + " ";
                throw JsErrorException.typeError("Class constructor " + n + "cannot be invoked without 'new'");
            }
            Object[] args = evalCallArgs(fnArgsNode, context);
            // An argument that threw means there is no call to make. evalSuperCall already does
            // this; every other call site did not, so `f(mustSucceed())` invoked f anyway.
            if (context.isError()) {
                return Terms.UNDEFINED;
            }
            // Convert JS types to Java types if JS/Java boundary:
            // - undefined → null
            // - JsValue (JsDate, etc.) → unwrapped via getJavaValue()
            if (callable.isExternal()) {
                for (int i = 0; i < args.length; i++) {
                    Object arg = args[i];
                    if (arg == Terms.UNDEFINED) {

View on GitHub (pinned to a22eb90246)