karatelabs/karate · error · JsErrorException

Property descriptor must be an object

Error message

Property descriptor must be an object

What it means

The third argument to Object.defineProperty must be a property descriptor object. Passing null or undefined as the descriptor throws this TypeError, per the spec's ToPropertyDescriptor which requires an Object.

Solutions

  1. Provide a valid descriptor object, e.g. {value: ..., writable: true, enumerable: true, configurable: true}
  2. Default an empty descriptor: desc ?? {value: undefined, writable: true, configurable: true}
  3. Fix the descriptor lookup that returned null/undefined

Example fix

// before
const desc = descriptors[key]; // may be undefined
Object.defineProperty(obj, key, desc); // throws
// after
const desc = descriptors[key] ?? {value: undefined, writable: true, enumerable: true, configurable: true};
Object.defineProperty(obj, key, desc);
Defensive patterns

Strategy: validation

Validate before calling

if (desc == null) throw new TypeError('descriptor object required before defineProperty');

Type guard

function isDescriptor(d) { return d != null && typeof d === 'object'; }

Try / catch

try { Object.defineProperty(obj, key, desc); }
catch (e) { if (String(e).includes('descriptor must be an object')) { Object.defineProperty(obj, key, desc ?? {}); } else { throw e; } }

Prevention

When it happens

Trigger: Object.defineProperty(obj, 'x', null), Object.defineProperty(obj, 'x', undefined), or a descriptor variable that failed to initialize (e.g. descriptors[key] returned undefined).

Common situations: Building descriptors dynamically from a lookup table with missing entries; API responses where the descriptor field is null; calling with only two arguments in a loosely-typed script.

Related errors


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

Appendix: source

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

    /** Returns the own {@link AccessorSlot} at {@code key} when one exists,
     *  {@code null} otherwise (own data property, missing, or unsupported
     *  type). Scoped to own properties only — for the chain-walking variant
     *  see {@code PropertyAccess#findAccessorInChain}. */
    private static AccessorSlot ownAccessorSlot(Object obj, String key) {
        PropertySlot s = PropertyAccess.ownSlot(obj, key);
        return s instanceof AccessorSlot acc ? acc : null;
    }

    @SuppressWarnings("unchecked")
    Object defineProperty(Context context, Object[] args) {
        if (args.length < 1 || !(args[0] instanceof ObjectLike || args[0] instanceof Map)) {
            throw JsErrorException.typeError("Object.defineProperty called on non-object");
        }
        if (args.length < 2) {
            throw JsErrorException.typeError("property key is null");
        }
        if (args.length < 3 || args[2] == null || args[2] == Terms.UNDEFINED) {
            throw JsErrorException.typeError("Property descriptor must be an object");
        }
        // Spec ToPropertyKey: pass ctx so ObjectLike keys dispatch through
        // ToPrimitive(string) → ToString (test262 Object/defineProperty/
        // 15.2.3.6-2-{20,24,25,39,41,43,44,45,46,47,48} pass arrays / boxed
        // booleans / objects with overridden toString as the key arg).
        CoreContext keyCtx = context instanceof CoreContext c ? c : null;
        String prop = Terms.toPropertyKey(args[1], keyCtx);
        Object desc = args[2];
        ObjectLike descObj;
        Map<String, Object> descMap;
        if (desc instanceof ObjectLike ol) {
            descObj = ol;
            descMap = ol.toMap();
        } else if (desc instanceof Map) {
            descObj = null;
            descMap = (Map<String, Object>) desc;
        } else {
            throw JsErrorException.typeError("Property descriptor must be an object");

View on GitHub (pinned to a22eb90246)