karatelabs/karate · error · ParserException

duplicate private name

Error message

duplicate private name ${name} in class body

What it means

Within one class body, each private name must be unique, except that a getter/setter pair at the same placement (both static or both instance) may share one name. classPrivateScope records every private member name and its kind bits; a re-declaration that is not a legal accessor pair is rejected.

Solutions

  1. Remove or rename one of the duplicate private declarations
  2. If you want get/set semantics, make both members a matching accessor pair (same static-ness, one `get`, one `set`)
  3. Ensure the accessor pair's static/instance placement matches — a static getter with an instance setter is still a duplicate

Example fix

// before
class C { get #v() { return this._v; } static set #v(x) { this._v = x; } }
// after
class C { get #v() { return this._v; } set #v(x) { this._v = x; } }
Defensive patterns

Strategy: validation

Validate before calling

// Detect duplicate private declarations in a class body:
const names = (body.match(/#\w+/g) || []); const dup = names.filter((n,i)=>names.indexOf(n)!==i);

Try / catch

try { eval(js); } catch (e) { if (String(e).includes('duplicate private name')) { /* rename or make a get/set pair */ } throw e; }

Prevention

When it happens

Trigger: Declaring two private members with the same name in a class body — e.g. `class C { #x; #x() {} }`, duplicate `static #m` and `#m`, two methods, or a get/set pair mismatched in static-ness — detected when the second `kinds.put` returns a prior entry.

Common situations: Copy-paste duplication when adding private methods; accidentally adding a private field with the same name as an existing private method; moving a member between static/instance without removing the old one.

Related errors


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

Appendix: source

Thrown at karate-js/src/main/java/io/karatelabs/parser/JsParser.java:366

                }
            }
            if (key == null) {
                continue;
            }
            String name = key.getText();
            if ("#constructor".equals(name)) {
                throw new ParserException("#constructor is a reserved private name");
            }
            if (names == null) {
                names = new HashSet<>(4);
                kinds = new HashMap<>(4);
            }
            Integer prior = kinds.put(name, kind);
            // a get/set pair at the same placement is the sole legal duplicate
            boolean accessorPair = prior != null && (prior & 8) == (kind & 8)
                    && (prior & 4) == 0 && (kind & 4) == 0 && ((prior & 3) | (kind & 3)) == 3;
            if (prior != null && !accessorPair) {
                throw new ParserException("duplicate private name " + name + " in class body");
            }
            names.add(name);
        }
        return names == null ? parent : new PrivateScope(parent, names);
    }

    /**
     * An enclosing label. {@code iteration} records whether the labelled statement is an
     * iteration statement, which is what separates a legal {@code continue} target from a
     * legal {@code break} target (§14.13.1). An immutable cons cell so {@link #earlyErrors}
     * can thread the chain down without copying.
     */
    private record Label(String name, boolean iteration, Label next) {
        static boolean contains(Label chain, String name, boolean iterationOnly) {
            for (Label l = chain; l != null; l = l.next) {
                if (l.name.equals(name) && (!iterationOnly || l.iteration)) {
                    return true;
                }

View on GitHub (pinned to a22eb90246)