karatelabs/karate · error · JsErrorException
Method Promise.prototype called on incompatible receiver
Error message
Method Promise.prototype called on incompatible receiver
What it means
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.
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.
Example fix
// before const then = Promise.prototype.then; then(result); // after result.then((v) => ...);
Defensive patterns
Strategy: type-guard
Validate before calling
function safeThen(p, onF, onR) {
if (p == null || typeof p.then !== 'function') throw new TypeError('receiver is not a promise');
return p.then(onF, onR);
} Type guard
function isPromiseLike(v) { return v != null && typeof v.then === 'function'; } Try / catch
try { return value.then(cb); } catch (e) { if (e instanceof TypeError && /incompatible receiver/.test(e.message)) return Promise.resolve(value).then(cb); throw e; } Prevention
- 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.
When it happens
Trigger: `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.
Common situations: Borrowing Promise.prototype methods onto plain objects; passing prototype methods as callbacks without binding; JS transpilation artifacts that lose `this`.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Promise combinator called on a non-constructor
- <JS rejection reason>
- BigInt.prototype method called on non-BigInt
- this is not a Date object
- Error.prototype.toString called on non-object
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/1554eba8ae070b99.
Report an issue: GitHub.
Appendix: source
Thrown at karate-js/src/main/java/io/karatelabs/js/JsPromisePrototype.java:62
}
/**
* True while {@code value} is still the untampered {@code Promise.prototype.then}.
* The combinators' fast path (register a reaction directly, skipping the
* user-visible {@code Invoke(p, "then", …)}) is only valid while it holds —
* an own {@code then} on the element, or a replaced prototype method, is
* observable and must actually be called.
*/
static boolean isIntrinsicThen(Object value) {
return value instanceof JsBuiltinMethod method && method.delegate() == INSTANCE.thenDelegate;
}
private static JsPromise asPromise(Context context) {
Object thisObj = context.getThisObject();
if (thisObj instanceof JsPromise promise) {
return promise;
}
throw JsErrorException.typeError("Method Promise.prototype called on incompatible receiver");
}
private static JsCallable callableOrNull(Object value) {
return value instanceof JsCallable callable ? callable : null;
}
private Object then(Context context, Object[] args) {
JsPromise self = asPromise(context);
return then(self, callableOrNull(AsyncSupport.arg(args, 0)), callableOrNull(AsyncSupport.arg(args, 1)));
}
static JsPromise then(JsPromise self, JsCallable onFulfilled, JsCallable onRejected) {
JsPromise derived = new JsPromise(self.engine, self.scope);
self.addReaction((rejected, value) -> {
JsCallable handler = rejected ? onRejected : onFulfilled;
if (handler == null) {
// no handler for this outcome: pass it straight to the derived
// promise, which is now the one carrying the responsibilityView on GitHub (pinned to a22eb90246)