karatelabs/karate · error · ParserException
optional chain cannot be the operand of postfix ++/--
Error message
optional chain cannot be the operand of postfix ++/--
What it means
Postfix `++`/`--` require a valid simple assignment target; an optional chain (`a?.b++`) is never one. The Karate JS parser throws this early error when the postfix-update operand contains an optional chain. Split the read and the write instead.
Solutions
- Guard with an if: `if (a) a.b++;`
- Split the operation: `const v = a?.b; if (a) a.b = v + 1;`
- Use a definite reference when nullability is already handled: `a.b++`.
Example fix
// before counter?.count++; // after if (counter) counter.count++;
Defensive patterns
Strategy: validation
Validate before calling
// reject optional chains under postfix ++/--
function validPostfixUpdate(src) { return !/\?\.[\w$]+\s*(\+\+|--)/.test(src); } Try / catch
try { karate.eval(expr); } catch (e) { if (String(e).includes('optional chain cannot be the operand of postfix')) { /* guard with if */ } } Prevention
- Treat `?.` results as read-only values, never update targets.
- Guard updates with an explicit null check.
- Enable lint rules that flag assignment/update on optional chains.
When it happens
Trigger: Parsing `a?.b++` or `f()?.counter--`, detected in earlyErrorNodeChecks for MATH_POST_EXPR nodes whose operand subtree contains an optional chain.
Common situations: Incrementing a counter on a possibly-null object found via optional chaining; compact loop code written assuming `?.` composes with updates; mechanical rewrites from `a && a.b++`.
Related errors
- optional chain is not a valid assignment target
- optional chain cannot be the operand of prefix ++/--
- tagged template literal cannot follow an optional chain
- parser state: [ ]
- unary expression cannot be the base of '**'; wrap it in…
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/692ea507434ad249.
Report an issue: GitHub.
Appendix: source
Thrown at karate-js/src/main/java/io/karatelabs/parser/JsParser.java:523
Node base = stripExprWrappers(node.size() > 0 ? node.getFirst() : null);
if (base != null) {
boolean unaryBase = switch (base.type) {
case DELETE_EXPR, TYPEOF_EXPR, UNARY_EXPR, AWAIT_EXPR -> true;
case MATH_PRE_EXPR -> base.size() > 0 && base.getFirst().isToken()
&& (base.getFirst().token.type == PLUS || base.getFirst().token.type == MINUS);
default -> false;
};
if (unaryBase) {
throw new ParserException("unary expression cannot be the base of '**'; wrap it in parentheses");
}
}
}
case MATH_POST_EXPR -> {
// children: [operand, ++/--]
if (node.size() > 0) {
Node operand = node.getFirst();
if (sawOptionalChain && subtreeContainsOptionalChain(operand)) {
throw new ParserException("optional chain cannot be the operand of postfix ++/--");
}
checkSimpleAssignmentTarget(operand, "operand of postfix update", false);
}
}
case MATH_PRE_EXPR -> {
// children: [op, operand] — only ++/-- need a valid update target;
// unary +/- have no assignment side and parse the same shape.
if (node.size() > 1) {
Node op = node.getFirst();
if (op.isToken() && (op.token.type == PLUS_PLUS || op.token.type == MINUS_MINUS)) {
Node operand = node.get(1);
if (sawOptionalChain && subtreeContainsOptionalChain(operand)) {
throw new ParserException("optional chain cannot be the operand of prefix ++/--");
}
checkSimpleAssignmentTarget(operand, "operand of prefix update", false);
}
}
}View on GitHub (pinned to a22eb90246)