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

  1. Keep only one `__proto__:` entry in the literal.
  2. Set the prototype after construction: `Object.setPrototypeOf(obj, proto)`.
  3. Use `Object.create(proto)` when the prototype is the point of the object.
  4. 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

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


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. The

View on GitHub (pinned to a22eb90246)