{"record":{"id":"909432fa4773f906","repo":"karatelabs/karate","slug":"invalid-property-descriptor-cannot-both-specify-accessors","errorCode":null,"errorMessage":"Invalid property descriptor. Cannot both specify accessors and a value or writable attribute","messagePattern":"Invalid property descriptor\\. Cannot both specify accessors and a value or writable attribute","errorType":"exception","errorClass":"JsErrorException","httpStatus":null,"severity":"error","filePath":"karate-js/src/main/java/io/karatelabs/js/JsObjectConstructor.java","lineNumber":600,"sourceCode":"        // a {@code writable} field. Raw Java Maps have no proto — fall\n        // back to {@code containsKey}.\n        boolean hasGet = descHas(descObj, descMap, \"get\");\n        boolean hasSet = descHas(descObj, descMap, \"set\");\n        boolean hasValue = descHas(descObj, descMap, \"value\");\n        boolean hasWritable = descHas(descObj, descMap, \"writable\");\n        boolean hasEnumerable = descHas(descObj, descMap, \"enumerable\");\n        boolean hasConfigurable = descHas(descObj, descMap, \"configurable\");\n        boolean isAccessor = hasGet || hasSet;\n        boolean isData = hasValue || hasWritable;\n        // Generic descriptor — none of get/set/value/writable specified.\n        // Per spec ValidateAndApplyPropertyDescriptor: a generic descriptor\n        // preserves the descriptor *type* of the existing slot. The\n        // accessor → data overwrite (which clobbers the get/set fields) only\n        // fires when the new descriptor is itself a data descriptor; for a\n        // generic descriptor, only the attribute byte changes.\n        boolean isGeneric = !isAccessor && !isData;\n        if (isAccessor && isData) {\n            throw JsErrorException.typeError(\n                    \"Invalid property descriptor. Cannot both specify accessors and a value or writable attribute\");\n        }\n\n        Object target = args[0];\n        // Detected before the string-keyed checks below: `prop` is a symbol's\n        // descriptive string, which is not a key of the string store, so those\n        // checks would read the wrong slot (and reject on a frozen object).\n        JsSymbol symKey = JsSymbol.keyedBy(args[1]);\n        boolean keyExists = symKey == null && ownKeys(target).contains(prop);\n\n        // Extensibility check — ObjectLikes that don't model state inherit\n        // the perpetually-extensible default and pass through.\n        if (symKey == null && !keyExists && target instanceof ObjectLike ol && !ol.isExtensible()) {\n            throw JsErrorException.typeError(\"Cannot define property \" + prop + \", object is not extensible\");\n        }\n\n        // Validate accessor shapes early so we can fall through to the unified write below.\n        // Spec ToPropertyDescriptor §6.2.5.5: if Get/Set is present but not","sourceCodeStart":582,"sourceCodeEnd":618,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-js/src/main/java/io/karatelabs/js/JsObjectConstructor.java#L582-L618","documentation":"A property descriptor may not specify both accessor fields (get/set) and data fields (value/writable) at the same time. The spec's ToPropertyDescriptor rejects such a descriptor with a TypeError, and this engine reproduces that message exactly. This is a descriptor validation error — it fires regardless of the target property's current state.","triggerScenarios":"Object.defineProperty(obj, 'x', {get: fn, value: 1}), {set: fn, writable: false}, or a dynamically merged descriptor where one source contributes get/set and another contributes value/writable.","commonSituations":"Merging default descriptors with accessor configs (Object.assign({get}, {value})); copying fields from two different property definitions; refactoring a data property to a getter while leaving the old value/writable keys in place.","solutions":["Remove value/writable keys from descriptors that specify get/set (or vice versa)","When merging descriptors, strip incompatible fields based on whether the result should be data or accessor","Define the property twice if you truly need both forms (define accessor, then a separate object for data)"],"exampleFix":"// before\nObject.defineProperty(obj, 'x', {get: () => v, value: 1}); // throws\n// after\nObject.defineProperty(obj, 'x', {get: () => v, configurable: true}); // accessor only","handlingStrategy":"validation","validationCode":"const hasAccessor = 'get' in desc || 'set' in desc;\nconst hasData = 'value' in desc || 'writable' in desc;\nif (hasAccessor && hasData) throw new TypeError('descriptor cannot mix accessor and data fields');","typeGuard":"function isValidDescriptor(desc) {\n  const a = 'get' in desc || 'set' in desc;\n  const d = 'value' in desc || 'writable' in desc;\n  return !(a && d);\n}","tryCatchPattern":"try { Object.defineProperty(obj, key, desc); }\ncatch (e) { if (String(e).includes('Cannot both specify accessors')) { const {value, writable, ...accessorOnly} = desc; Object.defineProperty(obj, key, accessorOnly); } else { throw e; } }","preventionTips":["When merging descriptors, strip data fields if get/set are present","Keep separate constants for data vs accessor descriptor templates","Test dynamic descriptor construction for field overlap"],"tags":["javascript","object","property-descriptor","accessor"],"backgroundTag":"invalid-argument-value","analyzedSha":"a22eb90246d958d15a47bf436693d0121ad2812d","analyzedAt":"2026-09-12T09:01:00.220Z","contentChangedAt":"2026-09-12T09:01:00.220Z","schemaVersion":2},"datasetVersion":"2026-09-16T19:17:19.609Z"}