karatelabs/karate · error · ParserException
duplicate __proto__ in an object literal
Error message
duplicate __proto__ in an object literal
What it means
An object literal may contain at most one `__proto__: value` setter; a duplicate is an early error. The Karate JS parser detects multiple `__proto__` property definitions in one object literal and throws. Note this only applies to the `{ __proto__: v }` setter form, not computed keys or `'__proto__'` string keys.
Solutions
- Keep only one `__proto__:` entry in the literal.
- Set the prototype after construction: `Object.setPrototypeOf(obj, proto)`.
- Use `Object.create(proto)` when the prototype is the point of the object.
- If merging, filter out duplicate `__proto__` keys before building the literal.
Example fix
// before
const o = { __proto__: baseA, __proto__: baseB };
// after
const o = { __proto__: baseB }; // or Object.setPrototypeOf(Object.create(null), baseB) Defensive patterns
Strategy: validation
Validate before calling
// detect duplicate __proto__ setter in an object literal string
function singleProto(src) { return (src.match(/\b__proto__\s*:/g) || []).length <= 1; } Try / catch
try { karate.eval(objSrc); } catch (e) { if (String(e).includes('duplicate __proto__')) { /* keep one or use setPrototypeOf */ } } Prevention
- Prefer `Object.create(proto)` or `Object.setPrototypeOf` over `__proto__:` in literals.
- Filter `__proto__` keys when merging/generated object literals.
- Reserve `__proto__:` for one deliberate prototype assignment per literal.
When it happens
Trigger: Parsing `{ __proto__: a, __proto__: b }` or a literal where a spread/merge accidentally introduced a second `__proto__` key, detected by checkNoDuplicateProtoSetter.
Common situations: Merging two object literals by hand; code generation emitting a base `__proto__` plus a user-supplied one; copying properties including `__proto__` into a literal.
Related errors
- ${hint: names offending unquoted object-literal keys…
- parser state: [ ]
- optional chain is not a valid assignment target
- unary expression cannot be the base of '**'; wrap it in…
- optional chain cannot be the operand of postfix ++/--
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/75e464953ec8654a.
Report an issue: GitHub.
Appendix: source
Thrown at karate-js/src/main/java/io/karatelabs/parser/JsParser.java:642
return false;
}
// B.3.1 compares the StringValue, so escaped spellings
// ("__proto__") count — decode before comparing
return PROTO_KEY.equals(JsLexer.unescapeStringLiteral(text.substring(1, text.length() - 1)));
}
/** §13.2.5.1: at most one {@code __proto__: value} proto-setter per ObjectLiteral.
* Duplicates are legal in a destructuring pattern (the cover grammar drops the
* rule), so the caller gates on {@code !inPattern}. */
private static void checkNoDuplicateProtoSetter(Node litObject) {
boolean seen = false;
for (int i = 0, n = litObject.size(); i < n; i++) {
Node child = litObject.get(i);
if (child.isToken() || child.type != NodeType.OBJECT_ELEM || !isProtoSetter(child)) {
continue;
}
if (seen) {
throw new ParserException("duplicate __proto__ in an object literal");
}
seen = true;
}
}
// Descent state for checkStaticBlockBody.
private static final int SB_FN = 1; // inside a nested function — its own return / break target
private static final int SB_LOOP = 2; // inside an iteration statement of the block's own
private static final int SB_SWITCH = 4; // inside a switch statement of the block's own
private static final int SB_AWAIT_OK = 8; // inside a nested function's body — `await` is a name again
/**
* §15.7.1 early errors a class static initialization block carries. Every rule
* stops at the block boundary, so this descends that block's subtree only — a
* per-node helper of {@link #earlyErrors}, not a second traversal of the tree.
* <ul>
* <li>{@code return} has no target: the body is not a function body.</li>
* <li>{@code break} / {@code continue} may not reach out of the block. TheView on GitHub (pinned to a22eb90246)