{"record":{"id":"37c483540583f155","repo":"karatelabs/karate","slug":"tagged-template-literal-cannot-follow-an-optional-chain","errorCode":null,"errorMessage":"tagged template literal cannot follow an optional chain","messagePattern":"tagged template literal cannot follow an optional chain","errorType":"exception","errorClass":"ParserException","httpStatus":null,"severity":"error","filePath":"karate-js/src/main/java/io/karatelabs/parser/JsParser.java","lineNumber":545,"sourceCode":"            }\n            case MATH_PRE_EXPR -> {\n                // children: [op, operand] — only ++/-- need a valid update target;\n                // unary +/- have no assignment side and parse the same shape.\n                if (node.size() > 1) {\n                    Node op = node.getFirst();\n                    if (op.isToken() && (op.token.type == PLUS_PLUS || op.token.type == MINUS_MINUS)) {\n                        Node operand = node.get(1);\n                        if (sawOptionalChain && subtreeContainsOptionalChain(operand)) {\n                            throw new ParserException(\"optional chain cannot be the operand of prefix ++/--\");\n                        }\n                        checkSimpleAssignmentTarget(operand, \"operand of prefix update\", false);\n                    }\n                }\n            }\n            case FN_TAGGED_TEMPLATE_EXPR -> {\n                if (sawOptionalChain && node.size() > 0\n                        && subtreeContainsOptionalChain(node.getFirst())) {\n                    throw new ParserException(\"tagged template literal cannot follow an optional chain\");\n                }\n            }\n            // A FunctionDeclaration is a StatementListItem, never a Statement, so it\n            // may not be the sole body of an iteration statement (§13.7). Unlike the\n            // `if` clause (Annex B.3.4) there is no web-compat carve-out, so this is an\n            // early error in BOTH sloppy and strict code — hence it lives here, not in\n            // the strict-gated walk. `for (…) { function f(){} }` stays legal: the body\n            // Statement wraps a BLOCK, so its direct child is BLOCK, not FN_EXPR.\n            // A LexicalDeclaration (let/const) and a ClassDeclaration are likewise\n            // Declarations, not Statements, and — unlike FunctionDeclaration — have no\n            // Annex B carve-out for ANY clause, so they are illegal as the body of an\n            // `if`/`else` clause too (§13.6/§14.x). The `for`-init `let` is a direct\n            // FOR_STMT child, not a STATEMENT, so it is correctly ignored.\n            case FOR_STMT, WHILE_STMT, DO_WHILE_STMT -> {\n                checkNoFunctionDeclarationBody(node, \"a loop\");\n                checkNoLexicalOrClassDeclarationBody(node, \"a loop\");\n            }\n            case IF_STMT -> checkNoLexicalOrClassDeclarationBody(node, \"an `if`/`else` clause\");","sourceCodeStart":527,"sourceCodeEnd":563,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-js/src/main/java/io/karatelabs/parser/JsParser.java#L527-L563","documentation":"Per the ES spec, a tagged template cannot appear in an optional chain (`a?.b`tag`` is an early error). The Karate JS parser throws this when a tagged-template node's tag subtree contains an optional chain. Evaluate the chain first, then tag the resulting function.","triggerScenarios":"Parsing expressions like `api?.formatter`html`` or `f()?.tag`x``, detected in earlyErrorNodeChecks for FN_TAGGED_TEMPLATE_EXPR nodes whose first child contains an optional chain.","commonSituations":"Calling a tagged-template helper (e.g. html/sql template functions) retrieved through optional chaining; library code guarded with `?.` that is then used as a template tag.","solutions":["Extract the tag first: `const tag = a?.b; if (tag) tag`html`;`","Use a non-optional member access when the reference is guaranteed: `a.b`html```","Call via a wrapper: `(a?.b ?? defaultTag)`html``."],"exampleFix":"// before\nsvc?.renderer`<div/>`;\n// after\nconst renderer = svc?.renderer;\nif (renderer) renderer`<div/>`;","handlingStrategy":"validation","validationCode":"// reject tagged templates whose tag contains ?. \nfunction validTaggedTemplate(src) { return !/\\?\\.[\\w$]+\\s*`/.test(src); }","typeGuard":null,"tryCatchPattern":"try { karate.eval(expr); } catch (e) { if (String(e).includes('tagged template literal cannot follow an optional chain')) { /* extract tag into a variable */ } }","preventionTips":["Assign the tag function to a variable before tagging a template.","Treat template tags as requiring a definite reference.","Document in helper libraries that tags must not be accessed via `?.`."],"tags":["javascript","parser","syntax-error","optional-chaining","template-literals"],"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"}