karatelabs/karate · error · java.lang.RuntimeException
unexpected logical-assignment operator
Error message
unexpected logical-assignment operator: <operator>
What it means
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.
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.
Example fix
// before (script) foo ||= bar; // unexpected logical-assignment operator // after foo = foo || bar;
Defensive patterns
Strategy: validation
Validate before calling
// JS-side pre-check: only use supported logical-assignment operators
// supported: ||=, &&=, ??=
if (!/^(\|\|=|&&=|\?\?=)$/.test(opToken)) {
throw new Error('unsupported logical-assignment operator: ' + opToken);
} Type guard
function isLogicalAssignOp(tok) {
return tok === '||=' || tok === '&&=' || tok === '??=';
} Try / catch
try {
return karate.eval(script);
} catch (RuntimeException e) {
if (e.getMessage().contains("unexpected logical-assignment operator")) {
// rewrite as explicit binary form and retry
script = script.replace("||=", "= ...");
}
throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- unexpected operator
- parser state: [ ]
- too much recursion
- invalid shorthand initializer: only allowed in…
- unexpected private name
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/d367fc37ec79edc8.
Report an issue: GitHub.
Appendix: source
Thrown at karate-js/src/main/java/io/karatelabs/js/PropertyAccess.java:495
context.update(name, newValue, trackingNode);
return newValue;
}
private static Object incDecRefByName(Node node, CoreContext context, boolean isIncrement, boolean returnNew) {
String name = node.getText();
Object oldValue = context.get(name);
Object step = Terms.incDecStep(oldValue);
Object newValue = isIncrement ? Terms.add(oldValue, step, context) : Terms.min(oldValue, step, context);
context.update(name, newValue);
return returnNew ? newValue : oldValue;
}
private static boolean shouldLogicalAssign(TokenType operator, Object lhsValue) {
return switch (operator) {
case PIPE_PIPE_EQ -> !Terms.isTruthy(lhsValue);
case AMP_AMP_EQ -> Terms.isTruthy(lhsValue);
case QUES_QUES_EQ -> lhsValue == null || lhsValue == Terms.UNDEFINED;
default -> throw new RuntimeException("unexpected logical-assignment operator: " + operator);
};
}
private static Object logicalCompoundByIndex(Object object, Object index, TokenType operator, Node rhsNode, CoreContext context, Node trackingNode) {
if (index instanceof Number n) {
int i = denseIndex(n);
if (object instanceof List && i >= 0) {
List<Object> list = (List<Object>) object;
Object oldValue = i < list.size() ? list.get(i) : Terms.UNDEFINED;
if (!shouldLogicalAssign(operator, oldValue)) return oldValue;
Object newValue = Interpreter.eval(rhsNode, context);
if (context.isStopped()) return null;
if (object instanceof JsArray ja) {
ja.checkDensePad(i);
}
while (list.size() <= i) list.add(Terms.UNDEFINED);
list.set(i, newValue);
firePropertySet(context, String.valueOf(i), newValue, oldValue, object, trackingNode);View on GitHub (pinned to a22eb90246)