{"record":{"id":"13f84fafb01bb364","repo":"karatelabs/karate","slug":"assignment-to-constant-key","errorCode":null,"errorMessage":"assignment to constant: ${key}","messagePattern":"assignment to constant: (.+?)","errorType":"exception","errorClass":"JsErrorException","httpStatus":null,"severity":"error","filePath":"karate-js/src/main/java/io/karatelabs/js/CoreContext.java","lineNumber":570,"sourceCode":"            if (idx >= 0 && frame[idx] != SlotTable.UNDECLARED) {\n                updateSlot(idx, value, node);\n                return;\n            }\n            // undeclared: fall through — a pre-declaration write resolves an\n            // outer/global binding (or creates an implicit global), as today\n        }\n        BindingSlot s = resolve(key);\n        if (s == null) {\n            if (strict) {\n                // Strict mode forbids the sloppy implicit-global creation:\n                // assigning to an unresolvable name is a ReferenceError.\n                throw JsErrorException.referenceError(key + \" is not defined\");\n            }\n            assignImplicitGlobal(key, value, node);\n            return;\n        }\n        if (s.scope == BindScope.CONST && s.initialized) {\n            throw JsErrorException.typeError(\"assignment to constant: \" + key);\n        }\n        // NamedEvaluation (§13.15.2): when RHS is an anonymous function expression and\n        // LHS is an IdentifierRef, set fn.name from the identifier. Mirrors the\n        // declare-path hook above. Skipped for already-named functions so e.g.\n        // `g = someNamedFn` does not clobber the original name.\n        if (value instanceof JsFunction fn && (fn.name == null || fn.name.isEmpty())) {\n            fn.name = key;\n        }\n        Object oldValue = s.value;\n        s.initialized = true;\n        // Unified write — works whether the Slot lives in this context's\n        // bindings, in capturedBindings, in an outer context, or in root.\n        // Sibling closures sharing the same Slot reference see the new\n        // value immediately.\n        s.value = value;\n        if (root.listener != null) {\n            root.listener.onBind(BindEvent.assign(key, value, oldValue, this, node));\n        }","sourceCodeStart":552,"sourceCodeEnd":588,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-js/src/main/java/io/karatelabs/js/CoreContext.java#L552-L588","documentation":"Karate's JS engine throws this TypeError when script code assigns to a variable that was declared with `const` and already initialized. Per ECMAScript semantics, const bindings are single-assignment; reassignment is a static violation that the engine detects at runtime in its name-resolution path (CoreContext.update). The name in the message is the identifier the script tried to rebind.","triggerScenarios":"Script code does `const x = 1; x = 2;`, or assigns to a const via `set`/compound assignment (`x += 1`, `x++` flows through update/compoundRefByName/setByName which all funnel into update). Any path resolving a variable by name to a CONST-scoped, initialized slot and calling update hits this.","commonSituations":"Porting JS that used `var`/`let` to `const` but still mutates loop counters or accumulators; reusing a config constant as a temp variable inside karate script expressions; accidental shadowing mistakes where the developer thinks they declare a new variable but only assign.","solutions":["Rename the assignment target or change the declaration from const to let/var so rebinding is allowed","If mutation is intentional, declare a new const for the new value instead of reassigning (e.g. `const y = x + 1`)","If the constant lives in karate config (karate-config.js), move the mutation to a non-const variable","Refactor mutation into an object property or array element — property writes are not const-binding writes"],"exampleFix":"// before\nconst limit = 10;\nfor (limit = 0; limit < n; limit++) { ... }\n// after\nconst limit = 10;\nfor (let i = 0; i < n && i < limit; i++) { ... }","handlingStrategy":"validation","validationCode":"// before assigning, check the binding is not const in your script discipline:\n// prefer let for anything mutated; in karate JS blocks use `let` for counters\nlet limit = 10;","typeGuard":"function isMutableBinding(name, scope) { return typeof scope[name] !== 'undefined' && scope.__consts == null || (scope.__consts && !scope.__consts.has(name)); }","tryCatchPattern":"try { x = 2; } catch (e) { if (String(e).includes('assignment to constant')) { /* use a new variable */ } else { throw e; } }","preventionTips":["Default to let; use const only for values proven never reassigned","Enable linting (no-const-assign rule) in scripts under version control","Avoid reusing config constants as loop counters","Mutate object/array contents instead of rebinding const names"],"tags":["javascript","const","typeerror","assignment"],"backgroundTag":"invalid-state-transition","analyzedSha":"a22eb90246d958d15a47bf436693d0121ad2812d","analyzedAt":"2026-09-12T09:01:00.220Z","contentChangedAt":"2026-09-12T09:01:00.220Z","schemaVersion":2},"datasetVersion":"2026-09-16T19:17:19.609Z"}