{"record":{"id":"df772cfa5460c2e1","repo":"karatelabs/karate","slug":"promise-combinator-called-on-a-non-constructor","errorCode":null,"errorMessage":"Promise combinator called on a non-constructor","messagePattern":"Promise combinator called on a non-constructor","errorType":"exception","errorClass":"JsErrorException","httpStatus":null,"severity":"error","filePath":"karate-js/src/main/java/io/karatelabs/js/JsPromiseConstructor.java","lineNumber":219,"sourceCode":"\n    /**\n     * The combinator's {@code this} value, i.e. spec {@code C}. Honored so\n     * {@code Promise.all.call(C, …)} consults C's own {@code resolve}; an absent\n     * receiver (a Java host call) falls back to the intrinsic. The result promise\n     * is still always a native one — NewPromiseCapability over a subclass\n     * constructor is not modelled — but IsConstructor(C) is enforced, since that\n     * is the one part of the capability step observable from here.\n     */\n    private ObjectLike constructorOf(Context context) {\n        Object thisValue = context.getThisObject();\n        if (thisValue == null || thisValue == Terms.UNDEFINED || thisValue == this) {\n            return this;\n        }\n        if (thisValue instanceof ObjectLike obj\n                && thisValue instanceof JsCallable callable && callable.isConstructable()) {\n            return obj;\n        }\n        throw JsErrorException.typeError(\"Promise combinator called on a non-constructor\");\n    }\n\n    /**\n     * The spec's {@code Invoke(nextPromise, \"then\", «resolveElement, reject»)}.\n     * Fast path: the element is a native promise still resolving the untampered\n     * {@code Promise.prototype.then}, so nothing observable happens and the\n     * reaction can be registered on it directly. Otherwise {@code then} really is\n     * user code — read it and call it here, so a throwing getter or a throwing\n     * {@code then} aborts the combinator loop rather than being absorbed into\n     * the element promise (which, with an endless iterator, never terminates).\n     * <p>\n     * {@code intrinsic} says the value came from the untampered\n     * {@code Promise.resolve}, which always hands back a native promise. When it\n     * did not, the spec's {@code Invoke} is the whole story: whatever a custom\n     * {@code C.resolve} returned must have a callable {@code then}, and a\n     * primitive — or an object without one — is a {@code TypeError} that rejects\n     * the combinator, not something to wrap and fulfil with.\n     */","sourceCodeStart":201,"sourceCodeEnd":237,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-js/src/main/java/io/karatelabs/js/JsPromiseConstructor.java#L201-L237","documentation":"`Promise.all/race/allSettled/any` are generic: they use their `this` value as the constructor for building the result. The engine's `constructorOf` accepts either the intrinsic Promise or an ObjectLike that is a constructable callable (a subclass); anything else — e.g. `Promise.all.call(plainObj, ...)` or a `this` that lost constructability — throws this TypeError.","triggerScenarios":"`Promise.all.call(nonConstructor, iterable)`; invoking a combinator with `this` rebound to a plain object or non-constructable function; subclass patterns where the subclass fails `isConstructable()`.","commonSituations":"Borrowing Promise.all via .call/.apply from another object; broken subclassing (class without proper constructor semantics in the transpiled/host-bridged code).","solutions":["Call combinators normally: `Promise.all(iterable)` or `MySubclass.all(iterable)` where MySubclass extends Promise.","Don't rebind `this` with .call/.apply to a non-constructor value.","If using a subclass, ensure it is actually constructable (`new MySubclass(...)` works)."],"exampleFix":"// before\nconst out = Promise.all.call({}, promises);\n// after\nconst out = Promise.all(promises);","handlingStrategy":"type-guard","validationCode":"function safeAll(receiversCtor, iterable) {\n  const ctor = receiversCtor || Promise;\n  if (typeof ctor !== 'function' || !ctor.prototype) throw new TypeError('combinator receiver must be a constructor');\n  return Promise.all.call(ctor, iterable);\n}","typeGuard":"function isConstructable(f) { return typeof f === 'function' && !!f.prototype; }","tryCatchPattern":"try { return Promise.all(iterable); } catch (e) { if (e instanceof TypeError && /non-constructor/.test(e.message)) return Promise.all.call(Promise, iterable); throw e; }","preventionTips":["Avoid .call/.apply on Promise combinators unless subclassing intentionally.","Keep combinator receivers to Promise or subclasses of Promise.","Test custom subclasses with `new Sub(...)` before using them as combinator receivers."],"tags":["javascript","promise","combinator","this-binding"],"backgroundTag":"type-mismatch","analyzedSha":"a22eb90246d958d15a47bf436693d0121ad2812d","analyzedAt":"2026-09-12T09:01:00.220Z","contentChangedAt":"2026-09-12T09:01:00.220Z","schemaVersion":2},"datasetVersion":"2026-09-16T19:17:19.609Z"}