{"record":{"id":"43fdcdd3c52a8b8b","repo":"karatelabs/karate","slug":"get-regexp-prototype-called-on-incompatible-receiver","errorCode":null,"errorMessage":"get RegExp.prototype. called on incompatible receiver","messagePattern":"get RegExp\\.prototype\\. called on incompatible receiver","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"karate-js/src/main/java/io/karatelabs/js/JsRegexPrototype.java","lineNumber":79,"sourceCode":"        installFlagAccessor(\"dotAll\", Terms.UNDEFINED, r -> r.flags.contains(\"s\"));\n        installFlagAccessor(\"sticky\", Terms.UNDEFINED, r -> r.flags.contains(\"y\"));\n        installFlagAccessor(\"unicode\", Terms.UNDEFINED, r -> r.flags.contains(\"u\"));\n        installConstructor(\"RegExp\");\n    }\n\n    // Spec §22.2.6.4 etc. shared shape: TypeError on non-object receiver,\n    // sentinel on the prototype itself, extractor on a real JsRegex. Routes\n    // through {@link Prototype#installAccessor} so the descriptor lives in\n    // {@code builtins} and survives per-Engine reset (user-defined\n    // accessors via {@code Object.defineProperty} would still shadow these\n    // via {@code userProps}).\n    private void installFlagAccessor(String name, Object protoSentinel,\n                                      Function<JsRegex, Object> extractor) {\n        JsCallable lambda = (ctx, args) -> {\n            Object thisObj = ctx.getThisObject();\n            if (thisObj == this) return protoSentinel;\n            if (thisObj instanceof JsRegex r) return extractor.apply(r);\n            throw JsErrorException.typeError(\"get RegExp.prototype.\" + name + \" called on incompatible receiver\");\n        };\n        JsBuiltinMethod getter = new JsBuiltinMethod(\"get \" + name, 0, lambda);\n        installAccessor(name, getter, null, ACCESSOR_ATTRS);\n    }\n\n    // Spec preamble for test / exec — RegExp objects are required (no ToString\n    // coercion of `this`); the search string is ToString-coerced.\n    private static JsRegex requireRegex(Context context, String methodName) {\n        Object thisObj = context.getThisObject();\n        if (thisObj instanceof JsRegex r) return r;\n        throw JsErrorException.typeError(\"RegExp.prototype.\" + methodName + \" called on incompatible receiver\");\n    }\n\n    private static String argString(Object[] args, Context context) {\n        Object arg = args.length > 0 ? args[0] : Terms.UNDEFINED;\n        if (arg instanceof String s) return s;\n        if (arg instanceof JsString js) return js.text;\n        return Terms.toStringCoerce(arg, context instanceof CoreContext cc ? cc : null);","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-js/src/main/java/io/karatelabs/js/JsRegexPrototype.java#L61-L97","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","solutions":["Only read flags directly on real RegExp instances: re.global","If using getter.call(thisObj), ensure thisObj is an actual RegExp, or pass the prototype for the sentinel path","Duck-typed/mocked regexes must be real RegExp instances, or read flags via re.flags (string) on genuine regexes only"],"exampleFix":"// before\nconst global = Object.getOwnPropertyDescriptor(RegExp.prototype, 'global').get.call(fakeRegex);\n// after\nconst global = realRegex instanceof RegExp ? realRegex.global : false;","handlingStrategy":"type-guard","validationCode":"function getFlag(re, flag) {\n  if (!(re instanceof RegExp)) throw new Error('flag getter needs a real RegExp: ' + flag);\n  return Object.getOwnPropertyDescriptor(RegExp.prototype, flag).get.call(re);\n}","typeGuard":"const isRealRegex = v => v instanceof RegExp;\nconst flagOf = (re, f) => isRealRegex(re) ? re[f] : undefined;","tryCatchPattern":"try {\n  return flagGetter.call(thisObj);\n} catch (e) {\n  if (String(e.message).includes('incompatible receiver')) {\n    throw new Error('flag getter invoked on non-RegExp receiver');\n  }\n  throw e;\n}","preventionTips":["Read flags directly on real RegExp instances (re.global) instead of extracting getters","Ensure getter.call/apply receivers are genuine RegExp objects","Replace duck-typed mock regexes in tests with real RegExp instances","Remember these getters brand-check; they do not coerce or duck-type"],"tags":["javascript","regexp","typeerror","receiver"],"backgroundTag":"invalid-argument-value","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"}