karatelabs/karate · error · JsErrorException

Promise resolver is not a function

Error message

Promise resolver is not a function

What it means

`new Promise(executor)` requires the executor to be a function with `(resolve, reject)` parameters. When `AsyncSupport.arg(args, 0)` is anything other than a JsCallable (undefined, a value, a non-callable object), the engine throws this TypeError, matching the spec's IsCallable check.

Solutions

  1. Pass an executor function: `new Promise((resolve, reject) => ...)`.
  2. Guard the argument before constructing: `if (typeof executor !== 'function') throw ...`.
  3. Log or inspect the value if it should be a function — a failed definition above often leaves it undefined.

Example fix

// before
const p = new Promise(config.executor); // executor is a plain object here
// after
if (typeof config.executor === 'function') {
  const p = new Promise(config.executor);
}
Defensive patterns

Strategy: validation

Validate before calling

function safeNewPromise(executor) {
  if (typeof executor !== 'function') throw new TypeError('Promise executor must be a function');
  return new Promise(executor);
}

Type guard

function isCallable(v) { return typeof v === 'function'; }

Try / catch

try { const p = new Promise(executor); } catch (e) { if (e instanceof TypeError && /resolver is not a function/.test(e.message)) executor = defaultExecutor; throw e; }

Prevention

When it happens

Trigger: `new Promise()` with no argument; `new Promise(42)` or `new Promise({})`; passing a variable that was expected to be a function but is undefined due to a typo or earlier failure.

Common situations: Typos in callback names; forgetting to pass the executor during a refactor; dynamic executors loaded from data that turned out to be strings/values, not functions.

Related errors


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

Appendix: source

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

        }
        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;
        };
        AsyncSupport.Completion outcome = AsyncSupport.invoke(
                promise.engine, callable, new Object[]{resolveFn, rejectFn}, null);
        if (outcome.threw() && claimed.compareAndSet(false, true)) {

View on GitHub (pinned to a22eb90246)