{"record":{"id":"aac390c717ae59bf","repo":"karatelabs/karate","slug":"cannot-redefine-property-length","errorCode":null,"errorMessage":"Cannot redefine property: length","messagePattern":"Cannot redefine property: length","errorType":"exception","errorClass":"JsErrorException","httpStatus":null,"severity":"error","filePath":"karate-js/src/main/java/io/karatelabs/js/JsObjectConstructor.java","lineNumber":767,"sourceCode":"        if (isAccessor) {\n            // Merge with existing accessor: defining only `get` keeps the existing setter,\n            // and vice versa. Matches the literal path's merge behavior in evalLitObject.\n            JsCallable getter = newGetter;\n            JsCallable setter = newSetter;\n            if (existingIsAccessor) {\n                if (!hasGet) getter = existingAcc.getter;\n                if (!hasSet) setter = existingAcc.setter;\n            }\n            applyDefineAccessor(target, prop, getter, setter, newAttrs);\n        } else if (isArrayLength) {\n            // Skip the generic applyDefine path — defineLength bypasses the\n            // re-coercion that handleLengthAssign would run, since coercedLength\n            // already encodes the spec-validated Uint32.\n            boolean ok = ((JsArray) target).defineLength(coercedLength.intValue(), newAttrs);\n            if (!ok) {\n                // Non-configurable index in truncate range blocked the rest;\n                // partial-truncate already applied — report TypeError per spec.\n                throw JsErrorException.typeError(\"Cannot redefine property: length\");\n            }\n        } else if (isGeneric && existingIsAccessor) {\n            // Generic descriptor on existing accessor: spec preserves the\n            // accessor descriptor and only updates the attribute byte. Going\n            // through applyDefine here would clobber the AccessorSlot with a\n            // fresh DataSlot carrying undefined (test262\n            // {@code defineProperty/15.2.3.6-4-{59,82-7..24,272}}).\n            applyAttrsOnly(target, prop, newAttrs);\n        } else if (hasValue) {\n            applyDefine(target, prop, descRead(descObj, descMap, \"value\", cc), newAttrs);\n        } else if (!keyExists) {\n            // New key created via attribute-only descriptor: spec says value defaults\n            // to undefined.\n            applyDefine(target, prop, Terms.UNDEFINED, newAttrs);\n        } else if (existingIsAccessor) {\n            // Data descriptor (writable-only, no value) replacing an accessor:\n            // spec switches shape; value defaults to undefined.\n            applyDefine(target, prop, Terms.UNDEFINED, newAttrs);","sourceCodeStart":749,"sourceCodeEnd":785,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-js/src/main/java/io/karatelabs/js/JsObjectConstructor.java#L749-L785","documentation":"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.","triggerScenarios":"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.","commonSituations":"Freezing arrays then trying to shrink length; sealing arrays with Object.seal (which makes all elements non-configurable) then truncating.","solutions":["Do not freeze/seal arrays you intend to truncate; keep elements configurable","Truncate via slice/copy into a new array instead of redefining length","Remove or make configurable any non-configurable index below the target length first","Use arr.length = n assignment only on arrays with all-configurable elements"],"exampleFix":"// before\nconst arr = [1,2,3];\nObject.defineProperty(arr, 1, { value: 2, configurable: false });\nObject.defineProperty(arr, 'length', { value: 1 }); // TypeError\n// after\nconst arr = [1,2,3];\nconst short = arr.slice(0, 1); // new array, original untouched","handlingStrategy":"try-catch","validationCode":"const blocked = arr.some((v, i) => i < newLen && !Object.getOwnPropertyDescriptor(arr, i)?.configurable);","typeGuard":"function canTruncate(arr, newLen) { return arr.every((v, i) => i >= newLen || (Object.getOwnPropertyDescriptor(arr, i) || {}).configurable !== false); }","tryCatchPattern":"try { Object.defineProperty(arr, 'length', { value: newLen }); } catch (e) { if (String(e.message).includes('length')) arr = arr.slice(0, newLen); else throw e; }","preventionTips":["Do not freeze/seal arrays that need resizing; use readonly copies instead","Prefer slice/splice/push over redefining length","Keep array elements configurable when using Object.defineProperty on length"],"tags":["javascript","array","length","frozen"],"backgroundTag":"unsupported-operation","analyzedSha":"a22eb90246d958d15a47bf436693d0121ad2812d","analyzedAt":"2026-09-12T09:01:00.220Z","contentChangedAt":"2026-09-12T09:01:00.220Z","schemaVersion":2},"datasetVersion":"2026-09-19T12:17:13.211Z"}