karatelabs/karate · error · ParserException
optional chain cannot be the operand of prefix ++/--
Error message
optional chain cannot be the operand of prefix ++/--
What it means
Prefix `++`/`--` require a valid simple assignment target; an optional chain (`++a?.b`) is not one. The Karate JS parser throws this early error when a prefix-update operand subtree contains an optional chain. Guard or split the expression.
Solutions
- Guard with an if: `if (a) ++a.b;`
- Compute the value separately: `if (a) a.b = (a?.b ?? 0) + 1;`
- Drop the `?.` when the object is guaranteed non-nullish: `++a.b`.
Example fix
// before ++stats?.hits; // after if (stats) ++stats.hits;
Defensive patterns
Strategy: validation
Validate before calling
// reject optional chains under prefix ++/--
function validPrefixUpdate(src) { return !/(\+\+|--)\s*[\w$.]+\?\./.test(src); } Try / catch
try { karate.eval(expr); } catch (e) { if (String(e).includes('optional chain cannot be the operand of prefix')) { /* guard with if */ } } Prevention
- Never write `++`/`--` before a chain that contains `?.`.
- Split read-increment-write into explicit guarded statements.
- Review optional-chaining refactors for adjacent mutation operators.
When it happens
Trigger: Parsing `++a?.b` or `--obj?.count`, detected in earlyErrorNodeChecks for MATH_PRE_EXPR nodes where the operator is `++`/`--` and the operand contains `?.`.
Common situations: Same as postfix updates but with the operator in front — often from reordering code or from authors assuming `?.` yields a writable reference; common in refactored null-guard code.
Related errors
- optional chain is not a valid assignment target
- optional chain cannot be the operand of postfix ++/--
- 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/d253ae50a2c6cab2.
Report an issue: GitHub.
Appendix: source
Thrown at karate-js/src/main/java/io/karatelabs/parser/JsParser.java:536
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);
}
}
}
case FN_TAGGED_TEMPLATE_EXPR -> {
if (sawOptionalChain && node.size() > 0
&& subtreeContainsOptionalChain(node.getFirst())) {
throw new ParserException("tagged template literal cannot follow an optional chain");
}
}
// A FunctionDeclaration is a StatementListItem, never a Statement, so it
// may not be the sole body of an iteration statement (§13.7). Unlike the
// `if` clause (Annex B.3.4) there is no web-compat carve-out, so this is an
// early error in BOTH sloppy and strict code — hence it lives here, not in
// the strict-gated walk. `for (…) { function f(){} }` stays legal: the body
// Statement wraps a BLOCK, so its direct child is BLOCK, not FN_EXPR.
// A LexicalDeclaration (let/const) and a ClassDeclaration are likewiseView on GitHub (pinned to a22eb90246)