{"record":{"id":"5d328ad6a80a0ed6","repo":"karatelabs/karate","slug":"optional-chain-is-not-a-valid-assignment-target","errorCode":null,"errorMessage":"optional chain is not a valid assignment target","messagePattern":"optional chain is not a valid assignment target","errorType":"exception","errorClass":"ParserException","httpStatus":null,"severity":"error","filePath":"karate-js/src/main/java/io/karatelabs/parser/JsParser.java","lineNumber":490,"sourceCode":"     *   <li>IsValidSimpleAssignmentTarget: the LHS of {@code =} / compound assignment\n     *       and the operand of {@code ++}/{@code --} must be a simple reference\n     *       (identifier, member access) or a destructuring pattern. Patterns like\n     *       {@code (a + b) = 1}, {@code () => {} = 1}, {@code 1 = 1}, {@code ++f()},\n     *       {@code (x = y) = 1} are SyntaxErrors at parse phase.</li>\n     *   <li>Optional-chain restrictions: an OptionalExpression cannot be an\n     *       assignment target, the operand of {@code ++}/{@code --}, or the head of\n     *       a tagged template literal.</li>\n     * </ul>\n     * Throws {@link ParserException} so the test262 runner classifies the failure as\n     * {@code phase: parse}.\n     */\n    private void earlyErrorNodeChecks(Node node) {\n        switch (node.type) {\n            case ASSIGN_EXPR -> {\n                if (node.size() > 0) {\n                    Node lhs = node.getFirst();\n                    if (sawOptionalChain && subtreeContainsOptionalChain(lhs)) {\n                        throw new ParserException(\"optional chain is not a valid assignment target\");\n                    }\n                    // Logical-assignment operators (||=, &&=, ??=) are ES2021 and have no\n                    // Annex B web-compat carve-out for CallExpression LHS — the SimpleAssignmentTarget\n                    // requirement is strict. Plain `=` and the older compound operators still allow\n                    // `f() = …` in non-strict mode.\n                    TokenType op = node.size() > 1 && node.get(1).isToken() ? node.get(1).token.type : null;\n                    boolean noCallCarveOut = op == PIPE_PIPE_EQ || op == AMP_AMP_EQ || op == QUES_QUES_EQ;\n                    checkSimpleAssignmentTarget(lhs, \"assignment target\", noCallCarveOut);\n                }\n            }\n            case MATH_EXP_EXPR -> {\n                // §13.6: the base of `**` must be an UpdateExpression — an\n                // unparenthesized unary (-x, !x, ~x, +x, void x, typeof x,\n                // delete x.y, await x) is a SyntaxError; ++x / --x are legal.\n                Node base = stripExprWrappers(node.size() > 0 ? node.getFirst() : null);\n                if (base != null) {\n                    boolean unaryBase = switch (base.type) {\n                        case DELETE_EXPR, TYPEOF_EXPR, UNARY_EXPR, AWAIT_EXPR -> true;","sourceCodeStart":472,"sourceCodeEnd":508,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-js/src/main/java/io/karatelabs/parser/JsParser.java#L472-L508","documentation":"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.","triggerScenarios":"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.","commonSituations":"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`.","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."],"exampleFix":"// before\nobj?.prop = value;\n// after\nif (obj) obj.prop = value;","handlingStrategy":"validation","validationCode":"// reject assignments whose LHS uses optional chaining\nfunction validAssignTarget(src) { return !/\\?\\.[\\w$]+\\s*=[^=]/.test(src); }","typeGuard":null,"tryCatchPattern":"try { karate.eval(expr); } catch (e) { if (String(e).includes('optional chain is not a valid assignment target')) { /* rewrite expression */ } }","preventionTips":["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."],"tags":["javascript","parser","syntax-error","optional-chaining"],"backgroundTag":"javascript-syntax-error","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"}