karatelabs/karate · error · JsErrorException

assignment to constant

Error message

assignment to constant: ${frameTable.names[idx]}

What it means

Same TypeError as the by-name path but raised from the slot-indexed fast path (CoreContext.updateSlot): the engine resolved a local slot whose kind is KIND_CONST and the script attempted to store a new value into it. TDZ slots are exempt (they fail elsewhere); only initialized consts trigger this. The message names the slot's declared identifier.

Solutions

  1. Change the declaration from const to let where the value must be reassigned
  2. Use a separate mutable variable and keep the const as the fixed reference
  3. Replace increment/compound ops on consts with immutable expressions assigned to a new const
  4. For collections, mutate contents (`arr.push(x)`) rather than rebinding the const binding itself

Example fix

// before
const total = 0;
total += item.price;
// after
let total = 0;
total += item.price;
Defensive patterns

Strategy: validation

Validate before calling

let total = 0; // any variable later mutated must be let, not const

Try / catch

try { total += 1; } catch (e) { if (String(e).indexOf('assignment to constant') >= 0) { /* redeclare with let */ } else { throw e; } }

Prevention

When it happens

Trigger: Assignment, compound assignment, or pre/post increment/decrement (updateSlot is called by update, set, compound, logicalCompound, postIncDec, preIncDec) targeting a `const` local, e.g. `const total = 0; total += 5;` or `const c = []; c++`.

Common situations: Incrementing a const counter; `+=` on a const accumulator; refactor scripts where a `let` was turned into `const` but mutation code remained; generated/transpiled code emitting stores to const slots.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at karate-js/src/main/java/io/karatelabs/js/CoreContext.java:601

        // Sibling closures sharing the same Slot reference see the new
        // value immediately.
        s.value = value;
        if (root.listener != null) {
            root.listener.onBind(BindEvent.assign(key, value, oldValue, this, node));
        }
    }

    /** Slot analogue of the post-resolve half of {@link #update} — same const
     *  check, same name inference, same BindEvent. A TDZ slot behaves like the
     *  store's uninitialized binding: the write initializes (const included),
     *  and the event reports undefined as the old value, which is what the
     *  store held. Callers must have checked the slot is not UNDECLARED. */
    void updateSlot(int idx, Object value, Node node) {
        Object oldValue = frame[idx];
        if (oldValue == SlotTable.TDZ) {
            oldValue = Terms.UNDEFINED;
        } else if (frameTable.kinds[idx] == SlotTable.KIND_CONST) {
            throw JsErrorException.typeError("assignment to constant: " + frameTable.names[idx]);
        }
        if (value instanceof JsFunction fn && (fn.name == null || fn.name.isEmpty())) {
            fn.name = frameTable.names[idx];
        }
        frame[idx] = value;
        if (root.listener != null) {
            root.listener.onBind(BindEvent.assign(frameTable.names[idx], value, oldValue, this, node));
        }
    }

    private void assignImplicitGlobal(String key, Object value, Node node) {
        // ES6 non-strict implicit global: writes go straight to the engine's
        // single shared Bindings (root and script context point at the same
        // instance). No parent walk needed.
        root.bindings.putMember(key, value, null, true);
        if (root.listener != null) {
            root.listener.onBind(BindEvent.declare(key, value, BindScope.VAR, this, node));
        }

View on GitHub (pinned to a22eb90246)