karatelabs/karate · error · JsErrorException

Cannot assign to read only property

Error message

Cannot assign to read only property '{name}'

What it means

failReadOnly is the shared strict-mode rejection helper for string-keyed properties: putMember detects a non-writable slot and calls it, throwing the standard 'Cannot assign to read only property' TypeError as mandated by strict-mode [[Set]].

Solutions

  1. Unfreeze the object or define the property writable:true if mutation is intended
  2. Create a mutable copy (structuredClone / spread) instead of mutating the frozen original
  3. Guard with Object.getOwnPropertyDescriptor check before writing

Example fix

// before
var o = Object.freeze({mode: 'x'});
o.mode = 'y'; // TypeError
// after
var o = {mode: 'x'}; // keep mutable, or clone before writing
var copy = {...o}; copy.mode = 'y';
Defensive patterns

Strategy: validation

Validate before calling

var d = Object.getOwnPropertyDescriptor(o, name); if (d && !d.writable) throw new TypeError('prop not writable: ' + name);

Type guard

function isWritableProp(o, name) { var d = Object.getOwnPropertyDescriptor(o, name); return !d || d.writable === true; }

Try / catch

try { o[name] = v; } catch (e) { if (String(e).includes('read only property')) { o = {...o, [name]: v}; } else { throw e; } }

Prevention

When it happens

Trigger: Strict-mode obj.prop = value (or obj[name] = value) where prop exists with writable:false, or the object is frozen with the property still present.

Common situations: Object.freeze on constants/config objects then mutating them; Object.defineProperty omitting writable (defaults false); mutating imported/module-level frozen objects; strict-mode modules/functions in the Karate JS engine.

Related errors


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

Appendix: source

Thrown at karate-js/src/main/java/io/karatelabs/js/JsObject.java:478

    /** Removes the tombstone for {@code name} if any. Subclasses use this when
     *  a write reanimates a previously-deleted entry. */
    void clearTombstone(String name) {
        PropertySlot s = props == null ? null : props.get(name);
        if (s != null && s.tombstoned) {
            props.remove(name);
        }
    }

    /** True iff {@code name} has a non-tombstoned own slot (excludes intrinsics). */
    boolean ownContainsKey(String name) {
        PropertySlot s = props == null ? null : props.get(name);
        return s != null && !s.tombstoned;
    }

    /** Strict-mode [[Set]] rejection: assigning a non-writable / frozen prop. */
    static void failReadOnly(String name) {
        throw JsErrorException.typeError("Cannot assign to read only property '" + name + "'");
    }

    /** Strict-mode [[Set]] rejection: adding a key to a non-extensible object. */
    static void failNotExtensible(String name) {
        throw JsErrorException.typeError("Cannot add property " + name + ", object is not extensible");
    }

    /** Strict-mode [[Delete]] rejection: removing a non-configurable property. */
    static void failNotConfigurable(String name) {
        throw JsErrorException.typeError("Cannot delete property '" + name + "' of " + "[object Object]");
    }

    @Override
    public void putMember(String name, Object value) {
        putMember(name, value, null, false);
    }

    @Override

View on GitHub (pinned to a22eb90246)