karatelabs/karate · error · ParserException
optional chain is not a valid assignment target
Error message
optional chain is not a valid assignment target
What it means
This is a JavaScript early error: the left-hand side of an assignment is an optional chain (e.g. `a?.b = v`). Per the ES spec, an OptionalChain is never a valid SimpleAssignmentTarget, so the Karate JS parser rejects it at parse time. Rewrite the expression so the assignment target is a plain member or identifier reference.
Solutions
- Remove `?.` from the left-hand side of the assignment (use `a.b = v` when you know `a` is non-nullish).
- Guard the assignment: `if (a) a.b = v;` or `a && (a.b = v);`.
- If assigning into a nullable parent, compute the target first: `const t = a?.b;` then assign via a safe reference.
Example fix
// before obj?.prop = value; // after if (obj) obj.prop = value;
Defensive patterns
Strategy: validation
Validate before calling
// reject assignments whose LHS uses optional chaining
function validAssignTarget(src) { return !/\?\.[\w$]+\s*=[^=]/.test(src); } Try / catch
try { karate.eval(expr); } catch (e) { if (String(e).includes('optional chain is not a valid assignment target')) { /* rewrite expression */ } } Prevention
- Never combine `?.` with an assignment or update operator on the same chain.
- Use `if (x) x.y = v;` or `x && (x.y = v);` for guarded writes.
- Run a linter with the no-unsafe-optional-chaining / assignment rules enabled.
When it happens
Trigger: Parsing JS where an assignment (including `||=`, `&&=`, `??=`) has an LHS subtree containing `?.`, e.g. `a?.b = 1` or `f()?.x ??= 2`, detected in earlyErrorNodeChecks for ASSIGN_EXPR nodes.
Common situations: Typo where the author meant `a.b = 1` but typed `a?.b`; mechanically converting null-guard code to optional chaining without removing the assignment; refactoring `a && (a.b = 1)` into `a?.b = 1`.
Related errors
- optional chain cannot be the operand of postfix ++/--
- 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/5d328ad6a80a0ed6.
Report an issue: GitHub.
Appendix: source
Thrown at karate-js/src/main/java/io/karatelabs/parser/JsParser.java:490
* <li>IsValidSimpleAssignmentTarget: the LHS of {@code =} / compound assignment
* and the operand of {@code ++}/{@code --} must be a simple reference
* (identifier, member access) or a destructuring pattern. Patterns like
* {@code (a + b) = 1}, {@code () => {} = 1}, {@code 1 = 1}, {@code ++f()},
* {@code (x = y) = 1} are SyntaxErrors at parse phase.</li>
* <li>Optional-chain restrictions: an OptionalExpression cannot be an
* assignment target, the operand of {@code ++}/{@code --}, or the head of
* a tagged template literal.</li>
* </ul>
* Throws {@link ParserException} so the test262 runner classifies the failure as
* {@code phase: parse}.
*/
private void earlyErrorNodeChecks(Node node) {
switch (node.type) {
case ASSIGN_EXPR -> {
if (node.size() > 0) {
Node lhs = node.getFirst();
if (sawOptionalChain && subtreeContainsOptionalChain(lhs)) {
throw new ParserException("optional chain is not a valid assignment target");
}
// Logical-assignment operators (||=, &&=, ??=) are ES2021 and have no
// Annex B web-compat carve-out for CallExpression LHS — the SimpleAssignmentTarget
// requirement is strict. Plain `=` and the older compound operators still allow
// `f() = …` in non-strict mode.
TokenType op = node.size() > 1 && node.get(1).isToken() ? node.get(1).token.type : null;
boolean noCallCarveOut = op == PIPE_PIPE_EQ || op == AMP_AMP_EQ || op == QUES_QUES_EQ;
checkSimpleAssignmentTarget(lhs, "assignment target", noCallCarveOut);
}
}
case MATH_EXP_EXPR -> {
// §13.6: the base of `**` must be an UpdateExpression — an
// unparenthesized unary (-x, !x, ~x, +x, void x, typeof x,
// delete x.y, await x) is a SyntaxError; ++x / --x are legal.
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;View on GitHub (pinned to a22eb90246)