karatelabs/karate · error · ParserException
tagged template literal cannot follow an optional chain
Error message
tagged template literal cannot follow an optional chain
What it means
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.
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``.
Example fix
// before svc?.renderer`<div/>`; // after const renderer = svc?.renderer; if (renderer) renderer`<div/>`;
Defensive patterns
Strategy: validation
Validate before calling
// reject tagged templates whose tag contains ?.
function validTaggedTemplate(src) { return !/\?\.[\w$]+\s*`/.test(src); } Try / catch
try { karate.eval(expr); } catch (e) { if (String(e).includes('tagged template literal cannot follow an optional chain')) { /* extract tag into a variable */ } } Prevention
- 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 `?.`.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- optional chain is not a valid assignment target
- optional chain cannot be the operand of postfix ++/--
- optional chain cannot be the operand of prefix ++/--
- 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/37c483540583f155.
Report an issue: GitHub.
Appendix: source
Thrown at karate-js/src/main/java/io/karatelabs/parser/JsParser.java:545
}
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 likewise
// Declarations, not Statements, and — unlike FunctionDeclaration — have no
// Annex B carve-out for ANY clause, so they are illegal as the body of an
// `if`/`else` clause too (§13.6/§14.x). The `for`-init `let` is a direct
// FOR_STMT child, not a STATEMENT, so it is correctly ignored.
case FOR_STMT, WHILE_STMT, DO_WHILE_STMT -> {
checkNoFunctionDeclarationBody(node, "a loop");
checkNoLexicalOrClassDeclarationBody(node, "a loop");
}
case IF_STMT -> checkNoLexicalOrClassDeclarationBody(node, "an `if`/`else` clause");View on GitHub (pinned to a22eb90246)