karatelabs/karate · error · JsErrorException

Cannot define property , object is not extensible

Error message

Cannot define property , object is not extensible

What it means

Object.defineProperty() on a non-extensible ObjectLike target throws this TypeError when the property does not already exist. Once Object.preventExtensions()/freeze()/seal() has been applied, adding brand-new keys is forbidden by spec; existing keys can still be redefined (subject to their attributes). ObjectLikes that do not model extensibility state default to perpetually-extensible and pass through.

Solutions

  1. Remove the preventExtensions/seal/freeze call on the object, or apply it only after all properties are added
  2. Use Object.defineProperty on an existing property instead of adding a new one (redefinition of existing keys is allowed on sealed objects)
  3. Check Object.isExtensible(obj) before defining and skip or clone the object if frozen: define on a shallow copy instead
  4. If the freeze came from a library, configure it to not freeze, or work on your own copy

Example fix

// before
const cfg = Object.freeze({ host: 'x' });
Object.defineProperty(cfg, 'port', { value: 8080 }); // TypeError
// after
const cfg = Object.freeze({ host: 'x' });
const cfg2 = { ...cfg, port: 8080 }; // extend via copy instead
Defensive patterns

Strategy: validation

Validate before calling

if (!Object.isExtensible(obj)) throw new Error('target is not extensible; cannot add ' + key);

Type guard

function canDefineNew(obj, key) { return obj != null && Object.isExtensible(obj) && !(key in obj); }

Try / catch

try { Object.defineProperty(obj, key, desc); } catch (e) { if (String(e.message).includes('not extensible')) obj = { ...obj, [key]: desc.value }; else throw e; }

Prevention

When it happens

Trigger: Calling Object.defineProperty(obj, 'newKey', {...}) where obj was made non-extensible via preventExtensions/seal/freeze and 'newKey' is not an own property. Also thrown for symbol keys through the parallel check at the JsObject branch.

Common situations: Freezing a config object for immutability then later trying to attach a cache field; sealing a shared module namespace; a library freezing an object the consumer then extends.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

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

        // fires when the new descriptor is itself a data descriptor; for a
        // generic descriptor, only the attribute byte changes.
        boolean isGeneric = !isAccessor && !isData;
        if (isAccessor && isData) {
            throw JsErrorException.typeError(
                    "Invalid property descriptor. Cannot both specify accessors and a value or writable attribute");
        }

        Object target = args[0];
        // Detected before the string-keyed checks below: `prop` is a symbol's
        // descriptive string, which is not a key of the string store, so those
        // checks would read the wrong slot (and reject on a frozen object).
        JsSymbol symKey = JsSymbol.keyedBy(args[1]);
        boolean keyExists = symKey == null && ownKeys(target).contains(prop);

        // Extensibility check — ObjectLikes that don't model state inherit
        // 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;

View on GitHub (pinned to a22eb90246)