karatelabs/karate · error · JsErrorException

Promise constructor cannot be invoked without 'new'

Error message

Promise constructor cannot be invoked without 'new'

What it means

Per the ES spec, Promise is a constructor and must be called with `new`. Karate checks the call's CallInfo flag `constructor`; a bare `Promise(...)` call reaches `call()` without constructor mode and throws this TypeError. This mirrors browsers/V8 exactly (`TypeError: Promise resolver X is not a constructor`-style rejection of missing new).

Solutions

  1. Add the `new` keyword: `new Promise((resolve, reject) => {...})`.
  2. If wrapping, bind through a helper that constructs: `const make = (ex) => new Promise(ex);`
  3. Search the script for direct `Promise(` call sites (no preceding new).

Example fix

// before
const p = Promise((resolve) => resolve(1));
// after
const p = new Promise((resolve) => resolve(1));
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof Promise !== 'function') throw new Error('Promise unavailable');
// always construct:
if (typeof executor !== 'function') throw new TypeError('executor must be a function');

Type guard

function isPromiseCtor(f) { return f === Promise; }

Try / catch

try { const p = new Promise(executor); } catch (e) { if (e instanceof TypeError) { /* missing 'new' or bad executor */ } throw e; }

Prevention

When it happens

Trigger: Writing `Promise(...)` or `Promise.call(...)` instead of `new Promise(...)`; forwarding Promise as a plain function reference, e.g. `const P = Promise; P(executor)`.

Common situations: Refactored code where `new` was dropped; destructured/aliased Promise; code translated from languages where object construction doesn't use `new`.

Related errors


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

Appendix: source

Thrown at karate-js/src/main/java/io/karatelabs/js/JsPromiseConstructor.java:86

    private static AsyncScope scopeOf(Engine engine) {
        AsyncScope scope = engine.currentScope();
        if (scope == null) {
            throw JsErrorException.typeError("Promise requires an active evaluation");
        }
        return scope;
    }

    static JsPromise newPromise(Context context) {
        Engine engine = engineOf(context);
        AsyncActivation.anyAsync = true;
        return new JsPromise(engine, scopeOf(engine));
    }

    @Override
    public Object call(Context context, Object[] args) {
        CallInfo callInfo = context.getCallInfo();
        if (callInfo == null || !callInfo.constructor) {
            throw JsErrorException.typeError("Promise constructor cannot be invoked without 'new'");
        }
        Object executor = AsyncSupport.arg(args, 0);
        if (!(executor instanceof JsCallable callable)) {
            throw JsErrorException.typeError("Promise resolver is not a function");
        }
        JsPromise promise = newPromise(context);
        AtomicBoolean claimed = new AtomicBoolean();
        JsCallable resolveFn = (ctx, a) -> {
            if (claimed.compareAndSet(false, true)) {
                AsyncSupport.settleFromCallback(promise, AsyncSupport.arg(a, 0), false);
            }
            return Terms.UNDEFINED;
        };
        JsCallable rejectFn = (ctx, a) -> {
            if (claimed.compareAndSet(false, true)) {
                AsyncSupport.settleFromCallback(promise, AsyncSupport.arg(a, 0), true);
            }
            return Terms.UNDEFINED;

View on GitHub (pinned to a22eb90246)