{"record":{"id":"1554eba8ae070b99","repo":"karatelabs/karate","slug":"method-promise-prototype-called-on-incompatible-receiver","errorCode":null,"errorMessage":"Method Promise.prototype called on incompatible receiver","messagePattern":"Method Promise\\.prototype called on incompatible receiver","errorType":"exception","errorClass":"JsErrorException","httpStatus":null,"severity":"error","filePath":"karate-js/src/main/java/io/karatelabs/js/JsPromisePrototype.java","lineNumber":62,"sourceCode":"    }\n\n    /**\n     * True while {@code value} is still the untampered {@code Promise.prototype.then}.\n     * The combinators' fast path (register a reaction directly, skipping the\n     * user-visible {@code Invoke(p, \"then\", …)}) is only valid while it holds —\n     * an own {@code then} on the element, or a replaced prototype method, is\n     * observable and must actually be called.\n     */\n    static boolean isIntrinsicThen(Object value) {\n        return value instanceof JsBuiltinMethod method && method.delegate() == INSTANCE.thenDelegate;\n    }\n\n    private static JsPromise asPromise(Context context) {\n        Object thisObj = context.getThisObject();\n        if (thisObj instanceof JsPromise promise) {\n            return promise;\n        }\n        throw JsErrorException.typeError(\"Method Promise.prototype called on incompatible receiver\");\n    }\n\n    private static JsCallable callableOrNull(Object value) {\n        return value instanceof JsCallable callable ? callable : null;\n    }\n\n    private Object then(Context context, Object[] args) {\n        JsPromise self = asPromise(context);\n        return then(self, callableOrNull(AsyncSupport.arg(args, 0)), callableOrNull(AsyncSupport.arg(args, 1)));\n    }\n\n    static JsPromise then(JsPromise self, JsCallable onFulfilled, JsCallable onRejected) {\n        JsPromise derived = new JsPromise(self.engine, self.scope);\n        self.addReaction((rejected, value) -> {\n            JsCallable handler = rejected ? onRejected : onFulfilled;\n            if (handler == null) {\n                // no handler for this outcome: pass it straight to the derived\n                // promise, which is now the one carrying the responsibility","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-js/src/main/java/io/karatelabs/js/JsPromisePrototype.java#L44-L80","documentation":"Every method on Promise.prototype funnels through `asPromise`, which narrows `this` to an actual JsPromise instance. If a method like `then`, `catch`, or `finally` is invoked with a `this` that is not an internal promise object (plain object, undefined, or a foreign thenable), the engine throws this TypeError — the standard 'incompatible receiver' guard.","triggerScenarios":"`const t = Promise.prototype.then; t(x)` with undefined this; `myObj.then = Promise.prototype.then; myObj.then(...)` on a non-promise object; destructuring then off a Promise instance and calling it detached.","commonSituations":"Borrowing Promise.prototype methods onto plain objects; passing prototype methods as callbacks without binding; JS transpilation artifacts that lose `this`.","solutions":["Only call promise methods on real promise instances: `promise.then(...)`.","If borrowing, wrap: `(p) => Promise.prototype.then.call(realPromise, ...)` — but ensure the receiver is genuinely a JsPromise.","Bind detached references: keep `then` attached to the instance instead of extracting it."],"exampleFix":"// before\nconst then = Promise.prototype.then;\nthen(result);\n// after\nresult.then((v) => ...);","handlingStrategy":"type-guard","validationCode":"function safeThen(p, onF, onR) {\n  if (p == null || typeof p.then !== 'function') throw new TypeError('receiver is not a promise');\n  return p.then(onF, onR);\n}","typeGuard":"function isPromiseLike(v) { return v != null && typeof v.then === 'function'; }","tryCatchPattern":"try { return value.then(cb); } catch (e) { if (e instanceof TypeError && /incompatible receiver/.test(e.message)) return Promise.resolve(value).then(cb); throw e; }","preventionTips":["Don't detach Promise.prototype methods from instances.","Avoid assigning Promise.prototype methods onto plain objects.","Wrap uncertain values with Promise.resolve() before using .then."],"tags":["javascript","promise","receiver","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"}