karatelabs/karate · error · JsErrorException (referenceError, via SlotTable.tdzError)
cannot access ' ' before initialization
Error message
cannot access '${node.getText()}' before initialization What it means
This is a JavaScript temporal-dead-zone (TDZ) TypeError raised while evaluating a compound assignment (e.g. `x += 1`). Karate's JS engine keeps `let`/`const` bindings in a SlotTable marked TDZ until their declaration executes; reading such a binding throws this error before any RHS side effects run, matching the ES2015+ spec order.
Solutions
- Move the `let`/`const` declaration above the first read of the variable
- Rename the later declaration or the earlier use to eliminate the same-scope redeclaration
- Convert the binding to `var` only if legacy hoisting semantics are truly intended
- Reorder initialization so top-level consts are set before functions that reference them run
Example fix
// before x += 1; let x = 2; // after let x = 2; x += 1;
Defensive patterns
Strategy: try-catch
Validate before calling
// JS (script under test)
if (typeof x === 'undefined' && declaredLater) { /* restructure */ }
// Java-side guard before eval: pre-scan script for let/const declared after use
code.lines().stream().filter(l -> l.matches(".*\\b(let|const)\\s+x\\b.*")) Type guard
function isInitialized(v) { return typeof v !== 'undefined'; }
// note: typeof cannot help inside TDZ; guard by ordering, not by typeof on the TDZ name Try / catch
try {
evalScript("x += 1;");
} catch (JsErrorException e) {
if (e.getMessage().contains("before initialization")) {
// re-declare earlier or initialize before use
} else throw e;
} Prevention
- Declare let/const at the top of their scope
- Avoid relying on function hoisting to access block-scoped variables
- Run scripts through a linter (no-use-before-define) before executing
- Prefer initializing defaults at declaration: `let x = def ?? 0;`
When it happens
Trigger: A `let`/`const` variable is read on the LHS of a compound assignment (`x += ...`, `x *= ...`) via its slot fast path in PropertyAccess.compound before the `let`/`const` declaration that initializes it has executed — e.g. `x += 1; let x = 2;` or a hoisted function body touching a later-declared const.
Common situations: Porting `var` code to `let`/`const` so hoisting no longer provides `undefined`; accidentally shadowing a variable declared later in the same block/scope; recursive or callback code that runs before a module-level const is initialized.
Related errors
- unexpected logical-assignment operator
- unexpected operator
- cannot access ' ' before initialization
- cannot access ' ' before initialization
- Unable to resolve global `this`
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/4972761c9621b2fb.
Report an issue: GitHub.
Appendix: source
Thrown at karate-js/src/main/java/io/karatelabs/js/PropertyAccess.java:362
}
/**
* Compound assignment (`op=`, e.g. +=, -=, *=) in spec §13.15 order:
* resolve the LHS Reference, GetValue it, and only then evaluate the RHS —
* a computed-key or getter side effect precedes any RHS side effect, and
* an abrupt completion at each step skips the rest. Returns the new value.
*/
static Object compound(Node node, CoreContext context, TokenType operator, Node rhsNode, Node trackingNode) {
return switch (node.type) {
case REF_EXPR -> {
// Slot fast path, in the same spec order as the name tail below: read the
// LHS (a TDZ read throws before any RHS side effect), THEN evaluate the RHS.
int slot = node.slot;
Object[] frame;
if (slot >= 0 && (frame = context.frame) != null && frame[slot] != SlotTable.UNDECLARED) {
Object oldValue = frame[slot];
if (oldValue == SlotTable.TDZ) {
throw SlotTable.tdzError(node.getText());
}
Object operand = Interpreter.eval(rhsNode, context);
if (context.isStopped()) yield Terms.UNDEFINED;
Object newValue = applyOperator(oldValue, operator, operand, context);
context.updateSlot(slot, newValue, trackingNode);
yield newValue;
}
yield compoundRefByName(node, context, operator, rhsNode, trackingNode);
}
case REF_DOT_EXPR, REF_BRACKET_EXPR -> {
AccessSite site = resolveWriteSite(node, context);
if (site == null || site == SHORT_CIRCUIT_SITE || context.isStopped()) yield Terms.UNDEFINED;
Object oldValue = site.privateName != null
? PrivateAccess.get(site.target, site.privateName, context)
: siteRead(site, context);
if (context.isStopped()) yield Terms.UNDEFINED;
Object operand = Interpreter.eval(rhsNode, context);
if (context.isStopped()) yield Terms.UNDEFINED;View on GitHub (pinned to a22eb90246)