karatelabs/karate · error · JsErrorException

Getter must be a function

Error message

Getter must be a function

What it means

When a property descriptor passed to Object.defineProperty() includes a 'get' field, that field must be a callable function or undefined. Any other value (including null) is rejected per spec ToPropertyDescriptor with a TypeError.

Solutions

  1. Pass an actual function to get: (obj, 'k', { get() { return this._k; } })
  2. Remove the get field entirely if you meant a data descriptor (use value/writable)
  3. Check for undefined vs null: use undefined (or omit the key), never null
  4. Verify the value is typeof 'function' before building the descriptor

Example fix

// before
Object.defineProperty(o, 'x', { get: null });
// after
Object.defineProperty(o, 'x', { get() { return this._x; } });
Defensive patterns

Strategy: type-guard

Validate before calling

if (desc.get !== undefined && typeof desc.get !== 'function') throw new TypeError('get must be a function or undefined');

Type guard

function hasValidGetter(desc) { return !('get' in desc) || desc.get === undefined || typeof desc.get === 'function'; }

Try / catch

try { Object.defineProperty(o, k, desc); } catch (e) { if (String(e.message).includes('Getter must be a function')) console.error('descriptor.get =', desc.get); else throw e; }

Prevention

When it happens

Trigger: Object.defineProperty(obj, 'k', { get: 123, enumerable: true }) or { get: null } — a present, non-undefined, non-callable 'get' value.

Common situations: Typo passing a property name instead of a function; refactoring from data descriptors to accessors and leaving a stale value in 'get'; JSON-serialized descriptors where functions became null.

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

Appendix: source

Thrown at karate-js/src/main/java/io/karatelabs/js/JsObjectConstructor.java:630

        // the perpetually-extensible default and pass through.
        if (symKey == null && !keyExists && target instanceof ObjectLike ol && !ol.isExtensible()) {
            throw JsErrorException.typeError("Cannot define property " + prop + ", object is not extensible");
        }

        // Validate accessor shapes early so we can fall through to the unified write below.
        // Spec ToPropertyDescriptor §6.2.5.5: if Get/Set is present but not
        // {@code undefined} AND not callable, throw TypeError. {@code null}
        // is a non-callable non-undefined value — must throw (test262
        // {@code defineProperty/15.2.3.6-3-{8,13}}). Only literal undefined
        // is the spec-permitted "absent" sentinel.
        JsCallable newGetter = null;
        JsCallable newSetter = null;
        if (isAccessor) {
            if (hasGet) {
                Object g = descRead(descObj, descMap, "get", cc);
                if (g != Terms.UNDEFINED) {
                    if (!(g instanceof JsCallable c)) {
                        throw JsErrorException.typeError("Getter must be a function");
                    }
                    newGetter = c;
                }
            }
            if (hasSet) {
                Object s = descRead(descObj, descMap, "set", cc);
                if (s != Terms.UNDEFINED) {
                    if (!(s instanceof JsCallable c)) {
                        throw JsErrorException.typeError("Setter must be a function");
                    }
                    newSetter = c;
                }
            }
        }

        // A symbol key addresses the symbol store — the validation below is
        // indexed by `prop`, which a symbol key does not have.
        if (symKey != null) {

View on GitHub (pinned to a22eb90246)