karatelabs/karate · error · JsErrorException

Constructor Map requires 'new'

Error message

Constructor Map requires 'new'

What it means

The Map constructor was invoked without `new` (e.g. `Map()` instead of `new Map()`). Karate's JS engine checks callInfo.constructor in JsMapConstructor.call and throws a TypeError because built-in constructors like Map cannot be called as plain functions (unlike, say, Object or Array).

Solutions

  1. Add the `new` keyword: `new Map()`.
  2. If you need call-or-construct flexibility, write `var m = x instanceof Map ? x : new Map(...)`.
  3. Ensure callbacks/wrappers that create maps use Reflect-style construction or an explicit arrow returning `new Map(...)`.
  4. Audit for other built-ins (WeakMap, Set) that share the same requirement.

Example fix

// before
var m = Map(entries); // TypeError
// after
var m = new Map(entries);
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof Map !== 'function') throw new Error('Map unavailable'); // construction sites must use new

Type guard

function isMap(v) { return v instanceof Map; }

Try / catch

try { var m = makeMap(entries); } catch (e) { if (String(e).indexOf("requires 'new'") !== -1) { var m2 = new Map(entries); } else { throw e; } }

Prevention

When it happens

Trigger: Calling Map() directly; passing Map as a callback where `new` semantics are lost (e.g. arr.map(Map)); refactors that dropped the `new` keyword; calling it through a wrapper that invokes without constructor context.

Common situations: Porting code patterns where new was optional for other constructors; default-parameter fallbacks like `options.map || Map()`; transpiled/minified code that stripped `new`.

Related errors


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

Appendix: source

Thrown at karate-js/src/main/java/io/karatelabs/js/JsMapConstructor.java:62

        defineOwn("prototype", JsMapPrototype.INSTANCE, PropertySlot.INTRINSIC);
        defineOwn("groupBy", new JsBuiltinMethod("groupBy", 2, (JsCallable) this::groupBy), METHOD_ATTRS);
    }

    private Object groupBy(Context context, Object[] args) {
        Object items = args.length > 0 ? args[0] : Terms.UNDEFINED;
        Object callback = args.length > 1 ? args[1] : Terms.UNDEFINED;
        // Spec: result is a fresh Map; keys carry -0 → +0 normalization (zero
        // coercion mode) — verified by negativeZero.js.
        return GroupByImpl.toMap(
                GroupByImpl.run(items, callback, /* propertyMode= */ false, context));
    }

    @Override
    public Object call(Context context, Object[] args) {
        CallInfo callInfo = context.getCallInfo();
        boolean isNew = callInfo != null && callInfo.constructor;
        if (!isNew) {
            throw JsErrorException.typeError("Constructor Map requires 'new'");
        }
        JsMap map = new JsMap();
        if (args.length == 0 || args[0] == null || args[0] == Terms.UNDEFINED) {
            return map;
        }
        // Look up `set` on the freshly-constructed map's prototype chain — honors
        // user-overridden Map.prototype.set per spec 24.1.1.1 step 9.
        Object setFn = map.getMember("set");
        if (!(setFn instanceof JsCallable adder)) {
            throw JsErrorException.typeError("Map.prototype.set is not callable");
        }
        JsIterator iter = IterUtils.getIterator(args[0], context);
        CoreContext cc = context instanceof CoreContext c ? c : null;
        Object savedThis = cc != null ? cc.thisObject : null;
        try {
            while (iter.hasNext()) {
                Object entry = iter.next();
                if (!(entry instanceof List) && !(entry instanceof ObjectLike)) {

View on GitHub (pinned to a22eb90246)