karatelabs/karate · error · ParserException

invalid : call expression

Error message

invalid ${siteName}: call expression

What it means

Assigning to a function call (`f() = 1`) is invalid in strict mode and under ES2021 logical-assignment operators. Karate implements the Annex B.3.5 web-compat carve-out for plain calls in non-strict mode, but when the caller passes noCallCarveOut=true (e.g. for `||=`, `&&=`, `??=`), call expressions on the left side are rejected.

Solutions

  1. Assign to a variable or property instead of a call result, then call again if needed
  2. Replace `f() ||= v` with an explicit check: `let r = f(); if (!r) r = v;`
  3. Remove `=`/logical-assignment after the call; call expressions produce values, not references

Example fix

// before
obj.getValue() ||= 42;
// after
if (obj.getValue() == null) { obj.setValue(42); }
Defensive patterns

Strategy: validation

Validate before calling

if (/\w\(\)\s*(\|\|=|&&=|\?\?=|=)/.test(code)) {
  throw new Error('assignment to call expression is not allowed here');
}

Type guard

function isCallTarget(node) {
  return node.type === 'AssignmentExpression' && node.left.type === 'CallExpression';
}

Try / catch

try {
  karate.eval(code);
} catch (e) {
  if (String(e.message).includes('call expression')) { /* rewrite assignment */ }
  throw e;
}

Prevention

When it happens

Trigger: Code like `f() ||= true`, `obj.get() &&= x`, or `g() ??= y`, or `f() = 1` inside strict-mode code where the call-carve-out is disabled.

Common situations: Using ES2021 logical assignment on the result of a getter or function call; refactoring `a.b() = ...` patterns from non-strict legacy code into strict contexts.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12). Data as JSON: /api/errors/e0a5455fe4e26855. Report an issue: GitHub.

Appendix: source

Thrown at karate-js/src/main/java/io/karatelabs/parser/JsParser.java:1742

                    throw new ParserException("invalid " + siteName + ": this");
                }
            }
            case REF_DOT_EXPR, REF_BRACKET_EXPR -> {
                // Plain member access; a `?.` would have been caught earlier by
                // the optional-chain branch above with a more specific message.
                // `import.meta` is a meta-property whose AssignmentTargetType is
                // invalid; `import` is not a reserved word in our lexer so it
                // parses as a normal REF_DOT_EXPR — flag the literal shape here.
                if (n.type == NodeType.REF_DOT_EXPR && isImportMeta(n)) {
                    throw new ParserException("invalid " + siteName + ": import.meta");
                }
            }
            case FN_CALL_EXPR -> {
                // Web-compat carve-out (Annex B B.3.5): allow `f() = 1` in non-strict mode.
                // Logical-assignment operators (||=, &&=, ??=) are ES2021 and the carve-out
                // does NOT apply to them; the caller passes noCallCarveOut=true in that case.
                if (noCallCarveOut) {
                    throw new ParserException("invalid " + siteName + ": call expression");
                }
            }
            default ->
                    throw new ParserException("invalid " + siteName + ": "
                            + n.type.name() + " is not a valid assignment target");
        }
    }

    /**
     * Strip thin {@code EXPR} / {@code EXPR_LIST} single-child wrappers introduced
     * by the parser so that callers can inspect the underlying expression shape.
     * Returns the node unchanged once the wrappers run out or the node has more
     * than one child.
     */
    private static Node stripExprWrappers(Node n) {
        while (n != null && n.size() == 1
                && (n.type == NodeType.EXPR || n.type == NodeType.EXPR_LIST)) {
            n = n.get(0);

View on GitHub (pinned to a22eb90246)