karatelabs/karate · error · JsErrorException (syntaxError)

private name is not declared in an enclosing class

Error message

private name ${name} is not declared in an enclosing class

What it means

In Karate's JS engine, `#name` private fields are tracked per-class in a private environment. `PrivateAccess.resolve` throws this SyntaxError when a `#name` reference is evaluated but no enclosing class declared that private name, mirroring the ES spec requirement that private names must be lexically declared in a surrounding class.

Solutions

  1. Declare the private name inside the enclosing class (add `#count;` or the field/method in the class body).
  2. Fix typos so the referenced private name exactly matches the declaration.
  3. Move the code back inside the class body so it is lexically enclosed by the declaration.
  4. If the field does not need privacy, convert `#name` to a normal property.

Example fix

// before
const f = obj.increment; // method using this.#count escapes class
f(); // SyntaxError: private name #count is not declared in an enclosing class
// after
obj.increment(); // called with proper class context / declare #count in the class body
Defensive patterns

Strategy: validation

Validate before calling

// before referencing #name, ensure the code is lexically inside the declaring class
function usesPrivateName(src, name) {
  return new RegExp('class[^{]*\\{[\\s\\S]*#' + name + '\\b').test(src);
}
if (!usesPrivateName(classSource, 'count')) throw new Error('#count not declared in enclosing class');

Type guard

function isInClassBody(fn) { return /class[\s\S]*#[A-Za-z_$]/.test(String(fn)); }

Try / catch

try { engine.eval(js); } catch (e) { if (String(e.message).includes('not declared in an enclosing class')) { /* fix source: declare the private name */ } else { throw e; } }

Prevention

When it happens

Trigger: Evaluating a JS expression that references a private name (e.g. `this.#count`) from code that is not lexically inside a class declaring `#count`, or referencing a private field of a class that only exists at runtime as a value (no enclosing declaration).

Common situations: Copy-pasting class methods out of their class body into standalone functions; calling a method that uses `#field` via `.call()`/function reassignment outside the class; typos in the private name (`#Count` vs `#count`).

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


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

Appendix: source

Thrown at karate-js/src/main/java/io/karatelabs/js/PrivateAccess.java:41

 */
package io.karatelabs.js;

/**
 * Read / write / brand-check for {@code obj.#x}. The parser has already proved the
 * name is declared by an enclosing class body, so an unresolved name here means the
 * frame is missing its private environment rather than that the program is wrong.
 * The runtime error that IS reachable is the brand miss: the object simply is not an
 * instance of the declaring class.
 */
final class PrivateAccess {

    private PrivateAccess() {
    }

    static PrivateName resolve(String name, CoreContext context) {
        PrivateName pn = context.privateEnv == null ? null : context.privateEnv.resolve(name);
        if (pn == null) {
            throw JsErrorException.syntaxError("private name " + name + " is not declared in an enclosing class");
        }
        return pn;
    }

    static boolean has(Object target, PrivateName pn) {
        return target instanceof JsObject obj && obj.hasPrivate(pn);
    }

    static Object get(Object target, PrivateName pn, CoreContext context) {
        JsObject obj = branded(target, pn, "read");
        return switch (pn.kind) {
            case FIELD -> obj.getPrivate(pn);
            case METHOD -> pn.method;
            case ACCESSOR -> {
                if (pn.getter == null) {
                    throw JsErrorException.typeError("'" + pn.name + "' was defined without a getter");
                }
                yield Interpreter.invokeGetter(pn.getter, target, context);

View on GitHub (pinned to a22eb90246)