karatelabs/karate · error · JsErrorException

Cannot assign to read only property

Error message

Cannot assign to read only property '{sym}'

What it means

In strict-mode JS code, assigning to a property that exists but is non-writable (e.g. defined with writable:false, or on a frozen object) must throw a TypeError per [[Set]]. Karate's JsObject.setSymbol enforces this for symbol-keyed properties; in sloppy mode the assignment is silently ignored.

Solutions

  1. Remove the freeze or the writable:false restriction if mutation is intended
  2. Use Object.defineProperty to redefine the property as writable before assigning
  3. Wrap the assignment in try/catch if it is best-effort

Example fix

// before
Object.defineProperty(o, sym, {value: 1, writable: false});
o[sym] = 2; // TypeError
// after
Object.defineProperty(o, sym, {value: 1, writable: true, configurable: true});
o[sym] = 2;
Defensive patterns

Strategy: try-catch

Validate before calling

var d = Object.getOwnPropertyDescriptor(o, sym); if (d && !d.writable) throw new TypeError('slot not writable');

Type guard

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

Try / catch

try { o[sym] = v; } catch (e) { if (String(e).includes('read only property')) { /* fallback: WeakMap metadata */ } else { throw e; } }

Prevention

When it happens

Trigger: Strict-mode assignment obj[mySymbol] = value where the symbol property was defined non-writable, or the object was frozen and the property resolved read-only.

Common situations: Freezing shared/constant objects for safety then writing symbol-keyed metadata; Object.defineProperty with enumerable:true but writable:false; library-internal symbol slots treated as user-writable.

Related errors


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

Appendix: source

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

     * and anything else creates an own default-attribute slot. Mirrors what
     * {@code setByName} + {@code putMember} do for string keys.
     */
    void setSymbol(JsSymbol sym, Object value, CoreContext ctx, boolean strict) {
        for (ObjectLike o = this; o != null; o = o.getPrototype()) {
            if (!(o instanceof JsObject jo)) {
                continue;
            }
            PropertySlot s = jo.ownSymbolSlot(sym);
            if (s == null) {
                continue;
            }
            if (s instanceof AccessorSlot acc) {
                acc.write(this, value, ctx, strict);
                return;
            }
            if (!s.isWritable()) {
                if (strict) {
                    throw JsErrorException.typeError(
                            "Cannot assign to read only property '" + sym + "'");
                }
                return;
            }
            if (jo == this) {
                s.write(this, value, ctx, strict);
                return;
            }
            break; // inherited writable data property — shadow it with an own slot
        }
        if (frozen || sealed || nonExtensible) {
            if (strict) {
                throw JsErrorException.typeError(
                        "Cannot add property " + sym + ", object is not extensible");
            }
            return;
        }
        defineOwnSymbol(sym, value, PropertySlot.ATTRS_DEFAULT);

View on GitHub (pinned to a22eb90246)