karatelabs/karate · error · JsErrorException

cannot access ' ' before initialization

Error message

cannot access '${key}' before initialization

What it means

This is a temporal-dead-zone (TDZ) ReferenceError: a `let`/`const` binding exists in the frame table but still holds the TDZ sentinel, meaning the declaration statement has not executed yet. Per the JS spec, reading such a binding must throw rather than return undefined.

Solutions

  1. Move the declaration above the first use of the variable.
  2. Rename the variable if an outer/global with the same name was intended.
  3. Convert to `var` only if legacy hoisting semantics are truly wanted (discouraged).
  4. Ensure deferred callbacks don't run before the declaration executes.

Example fix

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

Strategy: try-catch

Validate before calling

// ensure declaration executed before access
if (typeof x !== 'undefined') { /* safe only for var/global; TDZ still throws */ }

Try / catch

try { use(x); } catch (e) { if (e instanceof ReferenceError && e.message.includes('before initialization')) { /* declare x first or reorder */ } else throw e; }

Prevention

When it happens

Trigger: CoreContext.get(key) resolving a name whose frame slot equals SlotTable.TDZ — e.g. referencing a let/const variable above its declaration, or from a closure/handler invoked before initialization runs.

Common situations: Hoisting assumptions from `var` carried over to `let`/`const`; callbacks (setTimeout, event handlers) firing before the declaration line executes; use of a variable inside a function defined before the let/const statement.

Related errors


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

Appendix: source

Thrown at karate-js/src/main/java/io/karatelabs/js/CoreContext.java:338

    Object get(String key) {
        if ("this".equals(key)) {
            return thisObject;
        }
        if (callArgs != null && "arguments".equals(key)) {
            JsArray a = argumentsForRead();
            if (a != null) {
                return a;
            }
            // arrow frame with no enclosing function — ordinary resolution
        }
        if (frameTable != null) {
            int idx = frameTable.indexOf(key);
            if (idx >= 0) {
                Object v = frame[idx];
                if (v != SlotTable.UNDECLARED) {
                    if (v == SlotTable.TDZ) {
                        throw JsErrorException.referenceError("cannot access '" + key + "' before initialization");
                    }
                    return v;
                }
                // undeclared: fall through — pre-declaration the store lacks
                // the name too, so the legacy chain is the same resolution
            }
        }
        BindingSlot s = resolve(key);
        if (s == null) {
            return Terms.UNDEFINED;
        }
        return readSlot(s, key);
    }

    /** The `arguments` visible at this frame: the frame's own for a normal
     *  function call, the lexically enclosing function's for an arrow frame
     *  (§10.2.1.3 — arrows have no own `arguments`; identity is shared with
     *  the enclosing frame's object). Returns null for an arrow with no

View on GitHub (pinned to a22eb90246)