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
- Change the declaration from const to let where the value must be reassigned
- Use a separate mutable variable and keep the const as the fixed reference
- Replace increment/compound ops on consts with immutable expressions assigned to a new const
- 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
- Audit pre/post ++ and compound += on const declarations
- Run lint with no-const-assign over karate JS snippets
- Keep accumulation in let variables; keep totals-of-record in const
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
- assignment to constant
- Cannot set property ' ' which has only a getter
- Generator is already running
- groupBy called with null or undefined items
- groupBy callback is not a function
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)