karatelabs/karate · error · JsErrorException
Class constructor cannot be invoked without 'new
Error message
Class constructor ${n}cannot be invoked without 'new' What it means
Calling a class constructor as a plain function (no `new`) is a TypeError per ECMAScript; invokeCallable raises this when the callee is a JsFunctionNode whose isClassConstructor is true and newKeyword is false. The message includes the class name (with a trailing space when named).
Solutions
- Add `new` at the call site: `new MyClass(...)`
- If the call site is generic, branch on whether the value is a class before invoking
- Keep ES5 function-constructors as plain functions if they must be callable without new
- In dynamic/reflective host code, use the construct path rather than the plain-call path for classes
Example fix
// before const c = MyClass(1, 2); // throws // after const c = new MyClass(1, 2);
Defensive patterns
Strategy: type-guard
Validate before calling
if (typeof MyClass !== 'function' || MyClass.toString().trim().startsWith('class')) { /* must use new */ } Type guard
function isClassCtor(f) { return typeof f === 'function' && /^\s*class\s/.test(String(f)); } Try / catch
try { const c = MyClass(); } catch (e) { if (String(e).includes("cannot be invoked without 'new'")) { c = new MyClass(); } else { throw e; } } Prevention
- Always pair class declarations with new at call sites
- When refactoring function-constructors to classes, grep all call sites for missing new
- In dispatch tables, mark entries needing construction
When it happens
Trigger: `MyClass()` instead of `new MyClass()`; calling a class constructor via call/apply-style plain invocation; super-adjacent mistakes where a helper invokes the constructor directly; evaluating `typeof`-style debug calls that invoke the class.
Common situations: Migrating ES5 function-constructors to class syntax while old call sites (without new) remain; dynamic dispatch tables storing classes and invoking them without new; minified/generated code losing the new.
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
- is not a constructor
- Constructor Map requires 'new'
- Constructor WeakMap requires 'new'
- Constructor WeakSet requires 'new'
- Cannot set property ' ' which has only a getter
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/fc2f4e5220c3397b.
Report an issue: GitHub.
Appendix: source
Thrown at karate-js/src/main/java/io/karatelabs/js/Interpreter.java:653
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) {
args[i] = null;
} else if (arg instanceof JsValue jv && !(arg instanceof JsPrimitive)) {
// Unwrap JsValue (JsDate, JsUint8Array) but not JsPrimitive (Boolean/String/Number constructors)
args[i] = jv.getJavaValue();View on GitHub (pinned to a22eb90246)