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
- Remove or rename one of the duplicate private declarations
- If you want get/set semantics, make both members a matching accessor pair (same static-ness, one `get`, one `set`)
- 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
- Keep one declaration per private name; pair get/set only when intentional
- Ensure accessor pairs share the same static/instance placement
- Review copy-pasted class members for name collisions
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
- unexpected private name
- #constructor is a reserved private name
- identifier ' ' has already been declared
- unexpected logical-assignment operator
- unexpected operator
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)