karatelabs/karate · error · JsErrorException

Cannot redefine property: length

Error message

Cannot redefine property: length

What it means

Redefining the 'length' property of a JsArray via Object.defineProperty throws this TypeError when a non-configurable array index within the truncation range blocks the resize. Per spec, setting a smaller length must delete trailing elements; if any blocked element cannot be deleted, length is left partially truncated and a TypeError is raised.

Solutions

  1. Do not freeze/seal arrays you intend to truncate; keep elements configurable
  2. Truncate via slice/copy into a new array instead of redefining length
  3. Remove or make configurable any non-configurable index below the target length first
  4. Use arr.length = n assignment only on arrays with all-configurable elements

Example fix

// before
const arr = [1,2,3];
Object.defineProperty(arr, 1, { value: 2, configurable: false });
Object.defineProperty(arr, 'length', { value: 1 }); // TypeError
// after
const arr = [1,2,3];
const short = arr.slice(0, 1); // new array, original untouched
Defensive patterns

Strategy: try-catch

Validate before calling

const blocked = arr.some((v, i) => i < newLen && !Object.getOwnPropertyDescriptor(arr, i)?.configurable);

Type guard

function canTruncate(arr, newLen) { return arr.every((v, i) => i >= newLen || (Object.getOwnPropertyDescriptor(arr, i) || {}).configurable !== false); }

Try / catch

try { Object.defineProperty(arr, 'length', { value: newLen }); } catch (e) { if (String(e.message).includes('length')) arr = arr.slice(0, newLen); else throw e; }

Prevention

When it happens

Trigger: Object.defineProperty(arr, 'length', { value: smaller }) (or writable changes) on an array where an index < newLength is non-configurable, e.g. Object.defineProperty(arr, 0, {value:1, configurable:false}) beforehand, or on a frozen array.

Common situations: Freezing arrays then trying to shrink length; sealing arrays with Object.seal (which makes all elements non-configurable) then truncating.

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

Appendix: source

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

        if (isAccessor) {
            // Merge with existing accessor: defining only `get` keeps the existing setter,
            // and vice versa. Matches the literal path's merge behavior in evalLitObject.
            JsCallable getter = newGetter;
            JsCallable setter = newSetter;
            if (existingIsAccessor) {
                if (!hasGet) getter = existingAcc.getter;
                if (!hasSet) setter = existingAcc.setter;
            }
            applyDefineAccessor(target, prop, getter, setter, newAttrs);
        } else if (isArrayLength) {
            // Skip the generic applyDefine path — defineLength bypasses the
            // re-coercion that handleLengthAssign would run, since coercedLength
            // already encodes the spec-validated Uint32.
            boolean ok = ((JsArray) target).defineLength(coercedLength.intValue(), newAttrs);
            if (!ok) {
                // Non-configurable index in truncate range blocked the rest;
                // partial-truncate already applied — report TypeError per spec.
                throw JsErrorException.typeError("Cannot redefine property: length");
            }
        } else if (isGeneric && existingIsAccessor) {
            // Generic descriptor on existing accessor: spec preserves the
            // accessor descriptor and only updates the attribute byte. Going
            // through applyDefine here would clobber the AccessorSlot with a
            // fresh DataSlot carrying undefined (test262
            // {@code defineProperty/15.2.3.6-4-{59,82-7..24,272}}).
            applyAttrsOnly(target, prop, newAttrs);
        } else if (hasValue) {
            applyDefine(target, prop, descRead(descObj, descMap, "value", cc), newAttrs);
        } else if (!keyExists) {
            // New key created via attribute-only descriptor: spec says value defaults
            // to undefined.
            applyDefine(target, prop, Terms.UNDEFINED, newAttrs);
        } else if (existingIsAccessor) {
            // Data descriptor (writable-only, no value) replacing an accessor:
            // spec switches shape; value defaults to undefined.
            applyDefine(target, prop, Terms.UNDEFINED, newAttrs);

View on GitHub (pinned to a22eb90246)