{"record":{"id":"d367fc37ec79edc8","repo":"karatelabs/karate","slug":"unexpected-logical-assignment-operator-operator","errorCode":null,"errorMessage":"unexpected logical-assignment operator: <operator>","messagePattern":"unexpected logical-assignment operator: <operator>","errorType":"exception","errorClass":"java.lang.RuntimeException","httpStatus":null,"severity":"error","filePath":"karate-js/src/main/java/io/karatelabs/js/PropertyAccess.java","lineNumber":495,"sourceCode":"        context.update(name, newValue, trackingNode);\n        return newValue;\n    }\n\n    private static Object incDecRefByName(Node node, CoreContext context, boolean isIncrement, boolean returnNew) {\n        String name = node.getText();\n        Object oldValue = context.get(name);\n        Object step = Terms.incDecStep(oldValue);\n        Object newValue = isIncrement ? Terms.add(oldValue, step, context) : Terms.min(oldValue, step, context);\n        context.update(name, newValue);\n        return returnNew ? newValue : oldValue;\n    }\n\n    private static boolean shouldLogicalAssign(TokenType operator, Object lhsValue) {\n        return switch (operator) {\n            case PIPE_PIPE_EQ -> !Terms.isTruthy(lhsValue);\n            case AMP_AMP_EQ -> Terms.isTruthy(lhsValue);\n            case QUES_QUES_EQ -> lhsValue == null || lhsValue == Terms.UNDEFINED;\n            default -> throw new RuntimeException(\"unexpected logical-assignment operator: \" + operator);\n        };\n    }\n\n    private static Object logicalCompoundByIndex(Object object, Object index, TokenType operator, Node rhsNode, CoreContext context, Node trackingNode) {\n        if (index instanceof Number n) {\n            int i = denseIndex(n);\n            if (object instanceof List && i >= 0) {\n                List<Object> list = (List<Object>) object;\n                Object oldValue = i < list.size() ? list.get(i) : Terms.UNDEFINED;\n                if (!shouldLogicalAssign(operator, oldValue)) return oldValue;\n                Object newValue = Interpreter.eval(rhsNode, context);\n                if (context.isStopped()) return null;\n                if (object instanceof JsArray ja) {\n                    ja.checkDensePad(i);\n                }\n                while (list.size() <= i) list.add(Terms.UNDEFINED);\n                list.set(i, newValue);\n                firePropertySet(context, String.valueOf(i), newValue, oldValue, object, trackingNode);","sourceCodeStart":477,"sourceCodeEnd":513,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-js/src/main/java/io/karatelabs/js/PropertyAccess.java#L477-L513","documentation":"shouldLogicalAssign maps a logical-assignment operator (||=, &&=, ??=) to its truthiness rule; the default branch throws only if an operator token reached this code path that is not one of the three supported logical-assignment operators. This is an internal invariant guard in PropertyAccess's compound-assignment handling, not a user-facing validation.","triggerScenarios":"An assignment expression whose operator token is treated as a logical assignment by the parser but is not PIPE_PIPE_EQ, AMP_AMP_EQ, or QUES_QUES_EQ reaching logicalCompound/logicalCompoundByName/etc. — essentially a parser/grammar bug or an evaluation path misclassifying a compound operator.","commonSituations":"Rare; encountered when running a Karate version with a grammar/eval mismatch (e.g. a new compound operator added to the parser but not to PropertyAccess), or a malformed script that tickles the wrong dispatch path.","solutions":["Check the operator printed in the message; if it is a normal ||=, &&=, ??=, report/upgrade — this indicates a library bug.","Upgrade Karate to the latest patch version where the compound-assign dispatch is complete.","Rewrite the expression explicitly (a = a || b) to avoid the logical-assignment path."],"exampleFix":"// before (script)\nfoo ||= bar; // unexpected logical-assignment operator\n// after\nfoo = foo || bar;","handlingStrategy":"validation","validationCode":"// JS-side pre-check: only use supported logical-assignment operators\n// supported: ||=, &&=, ??=\nif (!/^(\\|\\|=|&&=|\\?\\?=)$/.test(opToken)) {\n    throw new Error('unsupported logical-assignment operator: ' + opToken);\n}","typeGuard":"function isLogicalAssignOp(tok) {\n    return tok === '||=' || tok === '&&=' || tok === '??=';\n}","tryCatchPattern":"try {\n    return karate.eval(script);\n} catch (RuntimeException e) {\n    if (e.getMessage().contains(\"unexpected logical-assignment operator\")) {\n        // rewrite as explicit binary form and retry\n        script = script.replace(\"||=\", \"= ...\");\n    }\n    throw e;\n}","preventionTips":["Stick to standard ||=, &&=, ??= forms in scripts.","Keep Karate versions of parser and JS engine in sync (upgrade together).","If hit with a standard operator, report a minimal reproducer — it is an internal dispatch bug."],"tags":["javascript","parser","internal-invariant","compound-assignment"],"backgroundTag":"internal-invariant-violation","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"}