{"record":{"id":"4b18a5518de97bbb","repo":"karatelabs/karate","slug":"unexpected-private-name-name-only-valid-as-the-left-operand","errorCode":null,"errorMessage":"unexpected private name '${name}': only valid as the left operand of `in`","messagePattern":"unexpected private name '(.+?)': only valid as the left operand of `in`","errorType":"exception","errorClass":"ParserException","httpStatus":null,"severity":"error","filePath":"karate-js/src/main/java/io/karatelabs/parser/JsParser.java","lineNumber":283,"sourceCode":"     *   <li>{@code delete obj.#x} is an error (§13.5.1), and a bare {@code #x} is\n     *       legal only as the left operand of {@code in}.</li>\n     * </ul>\n     */\n    private PrivateScope privateNodeChecks(Node node, PrivateScope privates) {\n        switch (node.type) {\n            case CLASS_EXPR -> {\n                return classPrivateScope(node, privates);\n            }\n            case REF_DOT_EXPR -> {\n                Node key = node.size() > 2 ? node.get(2) : null;\n                if (key != null && key.isToken() && key.token.type == PRIVATE_NAME) {\n                    requirePrivateDeclared(key.getText(), privates);\n                }\n            }\n            case PRIVATE_NAME_EXPR -> {\n                Node parent = node.getParent();\n                if (parent == null || parent.type != NodeType.IN_EXPR || parent.getFirst() != node) {\n                    throw new ParserException(\"unexpected private name '\" + node.getText()\n                            + \"': only valid as the left operand of `in`\");\n                }\n                requirePrivateDeclared(node.getText(), privates);\n            }\n            case DELETE_EXPR -> {\n                Node target = node.size() > 1 ? node.get(1) : null;\n                while (target != null && !target.isToken()) {\n                    if (target.type == NodeType.REF_DOT_EXPR) {\n                        Node key = target.size() > 2 ? target.get(2) : null;\n                        if (key != null && key.isToken() && key.token.type == PRIVATE_NAME) {\n                            throw new ParserException(\"cannot delete private member \" + key.getText());\n                        }\n                        break;\n                    }\n                    if (target.type == NodeType.PAREN_EXPR) {\n                        target = target.size() > 1 ? target.get(1) : null;\n                    } else if (target.type == NodeType.EXPR || target.type == NodeType.EXPR_LIST) {\n                        target = target.size() == 1 ? target.getFirst() : null;","sourceCodeStart":265,"sourceCodeEnd":301,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-js/src/main/java/io/karatelabs/parser/JsParser.java#L265-L301","documentation":"Private class names (`#name` in Karate's JS dialect, `#x` private fields) may only appear as the left operand of the `in` operator (existence check) inside the class that declares them. During the post-parse privateNodeChecks pass, any PRIVATE_NAME_EXPR node whose parent is not an IN_EXPR with itself as the first child is rejected. This enforces the spec rule that private fields cannot be accessed directly outside controlled positions.","triggerScenarios":"Evaluating a private name in any expression position other than `#name in obj` — e.g. `obj.#x`, `#x` standalone, `#x === 1`, or passing `#x` as a value — inside a class body, while childPrivates/privateNodeChecks walks the AST.","commonSituations":"Developers new to private fields trying to read or pass around a private member directly; refactoring code that moved a private name out of an `in` check; testing class internals from scripts outside the class scope.","solutions":["Use the membership test form: `#x in obj` to check for the private member's existence","Access the value through a public getter/method defined in the class instead of referencing `#x` directly","Ensure the private name is used inside the class that declares it; declare it if missing (otherwise the related 'not declared' error fires)"],"exampleFix":"// before\nif (obj.#count) { ... }\n// after\nif (#count in obj) { ... }","handlingStrategy":"validation","validationCode":"// Ensure private names appear only as `#name in obj`:\nconst bad = /(?<!in\\s)#\\w+/g; // find private names not preceded by `in` and review them","typeGuard":"const isPrivateInCheck = (expr) => /^#\\w+\\s+in\\s+/.test(expr.trim());","tryCatchPattern":"try { eval(js); } catch (e) { if (String(e).includes('unexpected private name')) { /* rewrite to `#x in obj` */ } throw e; }","preventionTips":["Use `#x in obj` for all private-member existence checks","Access private values only via class methods","Never treat private names as first-class expressions"],"tags":["javascript","parser","private-fields","classes"],"backgroundTag":"invalid-private-field-usage","analyzedSha":"a22eb90246d958d15a47bf436693d0121ad2812d","analyzedAt":"2026-09-12T09:01:00.220Z","contentChangedAt":"2026-09-12T09:01:00.220Z","schemaVersion":2},"datasetVersion":"2026-09-16T19:17:19.609Z"}