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
- Remove the second definition or make it compatible with the original descriptor (same data/accessor kind, same enumerable, no configurable:true).
- Don't pass configurable:false unless the property must be permanently locked; keep configurable:true so later redefinition is legal.
- Use Reflect.defineProperty inside a check with Object.getOwnPropertyDescriptor to compare the existing descriptor before defining.
- 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
- Keep configurable:true unless permanently locking a property
- Inspect the existing descriptor with Object.getOwnPropertyDescriptor before defineProperty
- Never re-run definition scripts against frozen/sealed objects
- Keep data vs accessor shape consistent across redefinitions
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
- Cannot set property ' ' which has only a getter
- assignment to constant
- assignment to constant
- Generator is already running
- groupBy called with null or undefined items
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)