karatelabs/karate · error · JsErrorException

: Expecting function

Error message

: Expecting function

What it means

Object.prototype.__defineGetter__ / __defineSetter__ require the second argument to be a callable function. When it is not (missing, undefined, or any non-callable), a TypeError labelled with the method name is thrown. Per spec, IsCallable is checked before the key is coerced, so an object with a throwing toString is still rejected based on the function argument first.

Solutions

  1. Pass an actual function: obj.__defineGetter__('x', () => this._x)
  2. Migrate to the modern equivalent: Object.defineProperty(obj, 'x', { get: fn, configurable: true, enumerable: true })
  3. Guard with typeof fn === 'function' before calling
  4. Check argument order — for defineProperty the descriptor holds the getter; for __defineGetter__ it is arg 2

Example fix

// before
obj.__defineGetter__('x', getterName); // getterName is undefined
// after
obj.__defineGetter__('x', function () { return this._x; });
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof fn !== 'function') throw new TypeError('__defineGetter__/__defineSetter__ expects a function');

Type guard

function isCallable(v) { return typeof v === 'function'; }

Try / catch

try { obj.__defineGetter__(key, fn); } catch (e) { if (String(e.message).includes('Expecting function')) Object.defineProperty(obj, key, { get: defaultGetter, configurable: true }); else throw e; }

Prevention

When it happens

Trigger: obj.__defineGetter__('x') with no function argument; obj.__defineGetter__('x', notAFunction) such as a value, null, or a string.

Common situations: Legacy __defineGetter__ code ported from old scripts where the getter was a variable that became undefined; copy/paste from code using defineProperty where argument positions differ.

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/853df7129286fa5d. Report an issue: GitHub.

Appendix: source

Thrown at karate-js/src/main/java/io/karatelabs/js/JsObjectPrototype.java:130

        // IsCallable(getter/setter) on define*, then ToPropertyKey(P).
        install("__defineGetter__", 2, (ctx, args) -> defineAccessor(ctx, args, true));
        install("__defineSetter__", 2, (ctx, args) -> defineAccessor(ctx, args, false));
        install("__lookupGetter__", 1, (ctx, args) -> lookupAccessor(ctx, args, true));
        install("__lookupSetter__", 1, (ctx, args) -> lookupAccessor(ctx, args, false));
        // Spec §20.1.3.1: Object.prototype.constructor === Object — resolved
        // per access against the reading Engine's constructor instance.
        installConstructor("Object");
    }

    private static Object defineAccessor(Context context, Object[] args, boolean isGetter) {
        String label = isGetter
                ? "Object.prototype.__defineGetter__"
                : "Object.prototype.__defineSetter__";
        Object thisObj = context.getThisObject();
        Terms.requireObjectCoercible(thisObj, label);
        Object fn = args.length < 2 ? Terms.UNDEFINED : args[1];
        if (!(fn instanceof JsCallable callable)) {
            throw JsErrorException.typeError(label + ": Expecting function");
        }
        // ToPropertyKey runs AFTER IsCallable per spec — the getter-non-callable
        // test asserts the key's toString is never called when the function
        // arg is rejected.
        String key = args.length == 0 ? "undefined" : Terms.toPropertyKey(args[0]);
        Map<String, Object> desc = new LinkedHashMap<>();
        desc.put(isGetter ? "get" : "set", callable);
        desc.put("enumerable", true);
        desc.put("configurable", true);
        JsObjectConstructor objectConstructor =
                (JsObjectConstructor) context.getEngine().builtinConstructor("Object");
        objectConstructor.defineProperty(context, new Object[]{thisObj, key, desc});
        return Terms.UNDEFINED;
    }

    private static Object lookupAccessor(Context context, Object[] args, boolean isGetter) {
        String label = isGetter
                ? "Object.prototype.__lookupGetter__"

View on GitHub (pinned to a22eb90246)