karatelabs/karate · error · JsErrorException

Setter must be a function

Error message

Setter must be a function

What it means

Symmetric to the getter check: a 'set' field present (and not undefined) in a property descriptor must be a callable function. Passing null, a value, or any non-callable throws this TypeError per ToPropertyDescriptor.

Solutions

  1. Pass a real function to set: (obj, 'k', { set(v) { this._k = v; } })
  2. Omit the set field for read-only accessor or data descriptors
  3. Use undefined (not null) if you need the key present but inert
  4. Guard with typeof desc.set === 'function' before defining

Example fix

// before
Object.defineProperty(o, 'x', { set: 42 });
// after
Object.defineProperty(o, 'x', { set(v) { this._x = v; } });
Defensive patterns

Strategy: type-guard

Validate before calling

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

Type guard

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

Try / catch

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

Prevention

When it happens

Trigger: Object.defineProperty(obj, 'k', { set: true }) or { set: null } or { set: someNonFunction }.

Common situations: Passing a stored callback variable that is actually undefined-transformed-to-null; mixing up value and set fields; descriptors round-tripped through JSON losing function identity.

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

Appendix: source

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

        // {@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) {
            if (!(target instanceof JsObject jo)) {
                return target;
            }
            PropertySlot prior = jo.ownSymbolSlot(symKey);
            if (prior == null && !jo.isExtensible()) {
                throw JsErrorException.typeError(
                        "Cannot define property " + symKey + ", object is not extensible");
            }
            // new keys default to all-false per ValidateAndApplyPropertyDescriptor

View on GitHub (pinned to a22eb90246)