karatelabs/karate · error · JsErrorException

Cannot delete property

Error message

Cannot delete property '{sym}'

What it means

Strict-mode delete on a property whose configurable attribute is false throws a TypeError per [[Delete]]; sloppy mode returns false silently. Karate's removeSymbol applies this to symbol-keyed own properties.

Solutions

  1. Define the symbol property with configurable:true if deletion must work
  2. Redefine the property to configurable:true before deleting
  3. Skip the delete (or guard with a configurability check) and treat it as intentional permanence

Example fix

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

Strategy: type-guard

Validate before calling

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

Type guard

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

Try / catch

try { delete o[sym]; } catch (e) { if (String(e).includes('Cannot delete property')) { o[sym] = undefined; } else { throw e; } }

Prevention

When it happens

Trigger: Strict-mode delete obj[sym] where sym names an own symbol property defined with configurable:false (e.g. via Object.defineProperty defaults) or created as non-configurable.

Common situations: Object.defineProperty without configurable:true (defaults to false); built-in/foreign objects exposing non-configurable symbol slots; cleanup code removing injected metadata in strict mode.

Related errors


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

Appendix: source

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

            s = as;
        } else {
            s = new AccessorSlot(sym.toString());
            symbols.put(sym, s);
        }
        s.getter = getter;
        s.setter = setter;
        s.attrs = attrs;
        s.tombstoned = false;
    }

    boolean removeSymbol(JsSymbol sym, boolean strict) {
        PropertySlot s = ownSymbolSlot(sym);
        if (s == null) {
            return true;
        }
        if (!s.isConfigurable()) {
            if (strict) {
                throw JsErrorException.typeError("Cannot delete property '" + sym + "'");
            }
            return false;
        }
        symbols.remove(sym);
        return true;
    }

    /** Own enumerable symbol keys — the partition §7.3.26 CopyDataProperties
     *  and {@code Object.assign} copy. */
    List<JsSymbol> ownEnumerableSymbols() {
        if (symbols == null) {
            return List.of();
        }
        List<JsSymbol> out = new ArrayList<>(symbols.size());
        for (Map.Entry<JsSymbol, PropertySlot> e : symbols.entrySet()) {
            PropertySlot slot = e.getValue();
            if (!slot.tombstoned && slot.isEnumerable()) {
                out.add(e.getKey());

View on GitHub (pinned to a22eb90246)