karatelabs/karate · error · JsErrorException

Class extends value is not a constructor or null

Error message

Class extends value is not a constructor or null

What it means

Thrown when a `class A extends B` has a non-null, non-undefined B that is not a constructable ObjectLike callable. Per spec, the extends value must be a constructor or null; primitives, plain values, or non-constructable callables are rejected at class-definition time.

Solutions

  1. Ensure the extends expression is a class (or regular function) defined before the derived class is evaluated.
  2. If inheritance should be optional, use `extends null` explicitly rather than an undefined-resolving variable.
  3. Log/inspect the extends value's type; primitives and plain objects cannot be extended.

Example fix

// before
var base = { hello: 'world' };
class A extends base {}
// after
class Base {}
class A extends Base {}
Defensive patterns

Strategy: validation

Validate before calling

if (Parent !== null && Parent !== undefined && (typeof Parent !== 'function')) throw new Error('extends value must be a constructor or null');

Type guard

function isValidExtends(v) { return v === null || v === undefined || typeof v === 'function'; }

Try / catch

try { eval('class A extends ' + name + ' {}'); } catch (e) { /* falls back to default base class */ }

Prevention

When it happens

Trigger: Evaluating `class A extends X` where X is a number, string, boolean, plain object, arrow function, or any non-constructable value. `class A extends undefined` is explicitly allowed.

Common situations: Import/order issues where the superclass variable is still a placeholder value; typos in class names; transpiled code inheriting from something that was a function in another engine but is not constructable here.

Related errors


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

Appendix: source

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

        }
        // heritage: `extends <expr>` — the EXPR follows the EXTENDS token.
        boolean hasExtends = false;
        Object parentValue = null;
        for (int i = 0, n = node.size(); i < n; i++) {
            Node child = node.get(i);
            if (child.token != null && child.token.type == EXTENDS) {
                hasExtends = true;
                parentValue = eval(node.get(i + 1), context);
                break;
            }
        }
        if (context.isError()) {
            return Terms.UNDEFINED;
        }
        ObjectLike parentCtor = null;
        if (hasExtends && parentValue != null && parentValue != Terms.UNDEFINED) {
            if (!(parentValue instanceof JsCallable pc) || !pc.isConstructable() || !(parentValue instanceof ObjectLike)) {
                throw JsErrorException.typeError("Class extends value is not a constructor or null");
            }
            parentCtor = (ObjectLike) parentValue;
        }
        Node ctorFnExpr = null;
        List<ClassMember> members = new ArrayList<>();
        for (int i = 0, n = node.size(); i < n; i++) {
            Node child = node.get(i);
            if (child.type == NodeType.CLASS_STATIC_BLOCK) {
                // rides the member list so it interleaves with the static field
                // initializers in source order
                ClassMember block = new ClassMember();
                block.isStatic = true;
                block.staticBlock = child;
                members.add(block);
                continue;
            }
            if (child.type != NodeType.CLASS_METHOD) {
                continue; // CLASS / EXTENDS / name / heritage EXPR / braces / semicolons

View on GitHub (pinned to a22eb90246)