karatelabs/karate · error · JsErrorException

Cannot redefine property:

Error message

Cannot redefine property: 

What it means

Object.defineProperty attempted to redefine an existing non-configurable property in a forbidden way: flipping configurable back to true, changing the enumerable flag, or switching between data and accessor forms. The spec (§ ordinaryDefineOwnProperty) forbids all of these once configurable is false, so the engine throws a TypeError.

Solutions

  1. Remove the second definition or make it compatible with the original descriptor (same data/accessor kind, same enumerable, no configurable:true).
  2. Don't pass configurable:false unless the property must be permanently locked; keep configurable:true so later redefinition is legal.
  3. Use Reflect.defineProperty inside a check with Object.getOwnPropertyDescriptor to compare the existing descriptor before defining.
  4. Recreate the object instead of redefining locked properties.

Example fix

// before
Object.defineProperty(o, 'id', {value: 1, configurable: false});
Object.defineProperty(o, 'id', {get: () => 1}); // throws
// after
Object.defineProperty(o, 'id', {value: 1, configurable: true});
Object.defineProperty(o, 'id', {get: () => 1}); // ok
Defensive patterns

Strategy: try-catch

Validate before calling

var desc = Object.getOwnPropertyDescriptor(o, 'id');
if (desc && !desc.configurable) {
  throw new Error('id is non-configurable; cannot redefine');
}

Type guard

function isRedefinable(o, key) { var d = Object.getOwnPropertyDescriptor(o, key); return !d || d.configurable; }

Try / catch

try { Object.defineProperty(o, 'id', newDesc); } catch (e) { if (String(e).indexOf('Cannot redefine property') !== -1) { /* keep original descriptor or rebuild object */ } else { throw e; } }

Prevention

When it happens

Trigger: Defining a property twice where the first defineProperty used configurable:false and the second changes configurable/enumerable or swaps value-data for get/set; redefining intrinsics or sealed/frozen object members.

Common situations: Making a property read-only (configurable:false) then later trying to attach a getter to it; applying a property-definition script twice against the same object; freezing an object then modifying its shape.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

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

    /**
     * Spec §10.1.6.3 ValidateAndApplyPropertyDescriptor — the rejection half, for
     * an existing NON-configurable property. Only two changes are legal: a
     * same-value redefine, and narrowing {@code writable} true → false on a data
     * property. Shared by the string- and symbol-keyed {@code defineProperty}
     * paths so the two cannot drift. {@code newValue} is a supplier because
     * reading the descriptor's {@code value} can run a getter, which must not
     * happen on a branch that doesn't need it.
     */
    private static void validateNonConfigurableRedefine(
            Object label, boolean existingIsAccessor, AccessorSlot existingAcc, Object existingValue,
            byte oldAttrs, byte newAttrs, boolean isAccessor, boolean isData,
            boolean hasConfigurable, boolean hasEnumerable, boolean hasGet, boolean hasSet,
            boolean hasValue, JsCallable newGetter, JsCallable newSetter,
            java.util.function.Supplier<Object> newValue) {
        // configurable cannot flip false → true
        if (hasConfigurable && (newAttrs & JsObject.CONFIGURABLE) != 0) {
            throw JsErrorException.typeError("Cannot redefine property: " + label);
        }
        // enumerable cannot change
        if (hasEnumerable && ((oldAttrs ^ newAttrs) & JsObject.ENUMERABLE) != 0) {
            throw JsErrorException.typeError("Cannot redefine property: " + label);
        }
        // Cannot switch between data and accessor shapes
        if (existingIsAccessor != isAccessor && (isAccessor || isData)) {
            throw JsErrorException.typeError("Cannot redefine property: " + label);
        }
        if (existingIsAccessor && isAccessor) {
            // Accessor→accessor: get / set cannot change unless they match the existing.
            JsCallable mergedGet = hasGet ? newGetter : existingAcc.getter;
            JsCallable mergedSet = hasSet ? newSetter : existingAcc.setter;
            if (mergedGet != existingAcc.getter || mergedSet != existingAcc.setter) {
                throw JsErrorException.typeError("Cannot redefine property: " + label);
            }
        } else if (!existingIsAccessor && isData) {
            // Data → data on non-configurable: writable cannot flip false → true.

View on GitHub (pinned to a22eb90246)