{"record":{"id":"4972761c9621b2fb","repo":"karatelabs/karate","slug":"cannot-access-node-gettext-before-initialization-497276","errorCode":null,"errorMessage":"cannot access '${node.getText()}' before initialization","messagePattern":"cannot access '(.+?)' before initialization","errorType":"exception","errorClass":"JsErrorException (referenceError, via SlotTable.tdzError)","httpStatus":null,"severity":"error","filePath":"karate-js/src/main/java/io/karatelabs/js/PropertyAccess.java","lineNumber":362,"sourceCode":"    }\n\n    /**\n     * Compound assignment (`op=`, e.g. +=, -=, *=) in spec §13.15 order:\n     * resolve the LHS Reference, GetValue it, and only then evaluate the RHS —\n     * a computed-key or getter side effect precedes any RHS side effect, and\n     * an abrupt completion at each step skips the rest. Returns the new value.\n     */\n    static Object compound(Node node, CoreContext context, TokenType operator, Node rhsNode, Node trackingNode) {\n                return switch (node.type) {\n            case REF_EXPR -> {\n                // Slot fast path, in the same spec order as the name tail below: read the\n                // LHS (a TDZ read throws before any RHS side effect), THEN evaluate the RHS.\n                int slot = node.slot;\n                Object[] frame;\n                if (slot >= 0 && (frame = context.frame) != null && frame[slot] != SlotTable.UNDECLARED) {\n                    Object oldValue = frame[slot];\n                    if (oldValue == SlotTable.TDZ) {\n                        throw SlotTable.tdzError(node.getText());\n                    }\n                    Object operand = Interpreter.eval(rhsNode, context);\n                    if (context.isStopped()) yield Terms.UNDEFINED;\n                    Object newValue = applyOperator(oldValue, operator, operand, context);\n                    context.updateSlot(slot, newValue, trackingNode);\n                    yield newValue;\n                }\n                yield compoundRefByName(node, context, operator, rhsNode, trackingNode);\n            }\n            case REF_DOT_EXPR, REF_BRACKET_EXPR -> {\n                AccessSite site = resolveWriteSite(node, context);\n                if (site == null || site == SHORT_CIRCUIT_SITE || context.isStopped()) yield Terms.UNDEFINED;\n                Object oldValue = site.privateName != null\n                        ? PrivateAccess.get(site.target, site.privateName, context)\n                        : siteRead(site, context);\n                if (context.isStopped()) yield Terms.UNDEFINED;\n                Object operand = Interpreter.eval(rhsNode, context);\n                if (context.isStopped()) yield Terms.UNDEFINED;","sourceCodeStart":344,"sourceCodeEnd":380,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-js/src/main/java/io/karatelabs/js/PropertyAccess.java#L344-L380","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nx += 1;\nlet x = 2;\n// after\nlet x = 2;\nx += 1;","handlingStrategy":"try-catch","validationCode":"// JS (script under test)\nif (typeof x === 'undefined' && declaredLater) { /* restructure */ }\n// Java-side guard before eval: pre-scan script for let/const declared after use\ncode.lines().stream().filter(l -> l.matches(\".*\\\\b(let|const)\\\\s+x\\\\b.*\"))","typeGuard":"function isInitialized(v) { return typeof v !== 'undefined'; }\n// note: typeof cannot help inside TDZ; guard by ordering, not by typeof on the TDZ name","tryCatchPattern":"try {\n  evalScript(\"x += 1;\");\n} catch (JsErrorException e) {\n  if (e.getMessage().contains(\"before initialization\")) {\n    // re-declare earlier or initialize before use\n  } else throw e;\n}","preventionTips":["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;`"],"tags":["javascript","tdz","compound-assignment"],"backgroundTag":"access-before-initialization","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"}