karatelabs/karate · error · TypeError

get RegExp.prototype. called on incompatible receiver

Error message

get RegExp.prototype. called on incompatible receiver

What it means

Flag getters like get RegExp.prototype.global are installed as accessors that accept only two receivers: the prototype object itself (returning a spec-defined sentinel like undefined) and actual JsRegex instances. For any other `this` (plain object, undefined in strict mode, a foreign regex-like object), a TypeError 'get RegExp.prototype.<flag> called on incompatible receiver' is thrown, matching spec-brand-check behavior for these getters.

Solutions

  1. Only read flags directly on real RegExp instances: re.global
  2. If using getter.call(thisObj), ensure thisObj is an actual RegExp, or pass the prototype for the sentinel path
  3. Duck-typed/mocked regexes must be real RegExp instances, or read flags via re.flags (string) on genuine regexes only

Example fix

// before
const global = Object.getOwnPropertyDescriptor(RegExp.prototype, 'global').get.call(fakeRegex);
// after
const global = realRegex instanceof RegExp ? realRegex.global : false;
Defensive patterns

Strategy: type-guard

Validate before calling

function getFlag(re, flag) {
  if (!(re instanceof RegExp)) throw new Error('flag getter needs a real RegExp: ' + flag);
  return Object.getOwnPropertyDescriptor(RegExp.prototype, flag).get.call(re);
}

Type guard

const isRealRegex = v => v instanceof RegExp;
const flagOf = (re, f) => isRealRegex(re) ? re[f] : undefined;

Try / catch

try {
  return flagGetter.call(thisObj);
} catch (e) {
  if (String(e.message).includes('incompatible receiver')) {
    throw new Error('flag getter invoked on non-RegExp receiver');
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling RegExp.prototype.global.call({}) or .apply(nonRegex) to read a flag off a fake regex object; destructuring the getter and invoking it with the wrong this; using a regex value that came from a different engine realm/wrapper and is not a JsRegex.

Common situations: Generic 'is this regex global?' helpers that apply flag getters to arbitrary values; duck-typed mock regexes in tests; `this` lost to undefined because a getter was extracted and called bare (e.g. const g = re.global.bind semantics misunderstood).

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at karate-js/src/main/java/io/karatelabs/js/JsRegexPrototype.java:79

        installFlagAccessor("dotAll", Terms.UNDEFINED, r -> r.flags.contains("s"));
        installFlagAccessor("sticky", Terms.UNDEFINED, r -> r.flags.contains("y"));
        installFlagAccessor("unicode", Terms.UNDEFINED, r -> r.flags.contains("u"));
        installConstructor("RegExp");
    }

    // Spec §22.2.6.4 etc. shared shape: TypeError on non-object receiver,
    // sentinel on the prototype itself, extractor on a real JsRegex. Routes
    // through {@link Prototype#installAccessor} so the descriptor lives in
    // {@code builtins} and survives per-Engine reset (user-defined
    // accessors via {@code Object.defineProperty} would still shadow these
    // via {@code userProps}).
    private void installFlagAccessor(String name, Object protoSentinel,
                                      Function<JsRegex, Object> extractor) {
        JsCallable lambda = (ctx, args) -> {
            Object thisObj = ctx.getThisObject();
            if (thisObj == this) return protoSentinel;
            if (thisObj instanceof JsRegex r) return extractor.apply(r);
            throw JsErrorException.typeError("get RegExp.prototype." + name + " called on incompatible receiver");
        };
        JsBuiltinMethod getter = new JsBuiltinMethod("get " + name, 0, lambda);
        installAccessor(name, getter, null, ACCESSOR_ATTRS);
    }

    // Spec preamble for test / exec — RegExp objects are required (no ToString
    // coercion of `this`); the search string is ToString-coerced.
    private static JsRegex requireRegex(Context context, String methodName) {
        Object thisObj = context.getThisObject();
        if (thisObj instanceof JsRegex r) return r;
        throw JsErrorException.typeError("RegExp.prototype." + methodName + " called on incompatible receiver");
    }

    private static String argString(Object[] args, Context context) {
        Object arg = args.length > 0 ? args[0] : Terms.UNDEFINED;
        if (arg instanceof String s) return s;
        if (arg instanceof JsString js) return js.text;
        return Terms.toStringCoerce(arg, context instanceof CoreContext cc ? cc : null);

View on GitHub (pinned to a22eb90246)