karatelabs/karate · error · JsErrorException

cannot access ' ' before initialization

Error message

cannot access '${node.getText()}' before initialization

What it means

Thrown when reading a let/const binding in its temporal dead zone — after the slot exists (script has entered its scope) but before the initializer has executed. SlotTable.TDZ marks the un-initialized slot; the produced ReferenceError names the variable.

Solutions

  1. Move the `let`/`const` declaration above all code that reads it in the same scope.
  2. Rename the variable if a same-scope `var`/outer binding was shadowed unintentionally.
  3. Defer function calls that read the binding until after its initializer runs.

Example fix

// before
console.log(x);
let x = 1;
// after
let x = 1;
console.log(x);
Defensive patterns

Strategy: validation

Validate before calling

if (typeof x === 'undefined' && declaredLater) throw new Error('x read before its let/const initialization');

Type guard

function isInitialized(fn) { try { fn(); return true; } catch (e) { return !String(e).includes('before initialization'); } }

Try / catch

try { use(x); } catch (e) { if (String(e).includes('before initialization')) { /* defer or redeclare */ } }

Prevention

When it happens

Trigger: Referencing a `let`/`const` variable earlier in the same scope than its declaration statement executes — e.g. using it inside a function called before the declaration line, or in its own initializer.

Common situations: Hoisting assumptions carried over from `var`; functions invoked during setup that touch bindings declared later; circular module-like top-level code in embedded scripts.

Related errors


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

Appendix: source

Thrown at karate-js/src/main/java/io/karatelabs/js/Interpreter.java:2583

        }
        String message = node.toStringError(rawMessage);
        boolean authored = context.isErrorAuthored();
        return new EngineException(message, null, errorName, jsMessage,
                authored, authored ? errorThrown : null, authored ? context.getErrorAuthoredLine() : 0);
    }

    private static Object evalRefExpr(Node node, CoreContext context) {
        if (node.getFirst().type == NodeType.FN_ARROW_EXPR) { // arrow function
            return evalFnArrowExpr(node.getFirst(), context);
        }
        int slot = node.slot;
        if (slot >= 0) {
            Object[] frame = context.frame;
            if (frame != null) {
                Object v = frame[slot];
                if (v != SlotTable.UNDECLARED) {
                    if (v == SlotTable.TDZ) {
                        throw SlotTable.tdzError(node.getText());
                    }
                    return v;
                }
            }
        }
        return evalRefExprByName(node, context);
    }

    // The name-keyed tail of evalRefExpr, outlined so the slot fast path above
    // stays small enough to inline reliably — an at-threshold hot method here
    // flips whole benchmark rows run to run depending on JIT timing.
    private static Object evalRefExprByName(Node node, CoreContext context) {
        String varName = node.getText();
        if ("this".equals(varName)) {
            return context.getThisObject();
        }
        if (context.callArgs != null && "arguments".equals(varName)) {
            JsArray a = context.argumentsForRead();

View on GitHub (pinned to a22eb90246)