karatelabs/karate · error · JsErrorException (typeError)

cannot apply compound assignment to

Error message

cannot apply compound assignment to: ${node}

What it means

A TypeError thrown when a compound-assignment expression (`+=`, `-=`, `*=`, etc.) is applied to a parse-tree node type the evaluator does not support as an assignment target. Only variable references (REF_EXPR) and property accesses (dot/bracket) are valid LHS forms; anything else reaches the switch's default arm.

Solutions

  1. Fix the script so the compound-assignment LHS is a plain variable or a property access
  2. Wrap the value in a variable first instead of assigning to a literal/expression
  3. If the LHS looks valid, check for parser/engine version regressions and upgrade

Example fix

// before
obj.getCounter() += 1;
// after
let c = obj.getCounter() + 1;
obj.setCounter(c);
Defensive patterns

Strategy: validation

Validate before calling

// Ensure compound-assignment LHS is an identifier or member expression before eval
if (!/^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*|\[[^\]]+\])*\s*(\+=|-=|\*=)/.test(code)) {
  throw new Error('invalid compound-assignment target');
}

Type guard

function isAssignableTarget(lhsNode) {
  return lhsNode && ['Identifier','MemberExpression'].includes(lhsNode.type);
}

Prevention

When it happens

Trigger: Compiling/executing a script where the LHS of `op=` parses as neither a variable reference nor a member access — typically invalid or malformed source such as `1 += 2`, a function call as the target, or a parser bug producing an unexpected node type.

Common situations: Hand-written or generated JS with a literal or call on the left of `=`; a syntax/parse regression feeding unexpected AST nodes into the interpreter.

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/d4fba775ce6bec04. Report an issue: GitHub.

Appendix: source

Thrown at karate-js/src/main/java/io/karatelabs/js/PropertyAccess.java:387

                }
                yield compoundRefByName(node, context, operator, rhsNode, trackingNode);
            }
            case REF_DOT_EXPR, REF_BRACKET_EXPR -> {
                AccessSite site = resolveWriteSite(node, context);
                if (site == null || site == SHORT_CIRCUIT_SITE || context.isStopped()) yield Terms.UNDEFINED;
                Object oldValue = site.privateName != null
                        ? PrivateAccess.get(site.target, site.privateName, context)
                        : siteRead(site, context);
                if (context.isStopped()) yield Terms.UNDEFINED;
                Object operand = Interpreter.eval(rhsNode, context);
                if (context.isStopped()) yield Terms.UNDEFINED;
                Object newValue = applyOperator(oldValue, operator, operand, context);
                if (context.isStopped()) yield Terms.UNDEFINED;
                if (site.privateName != null) PrivateAccess.set(site.target, site.privateName, newValue, context);
                else siteWrite(site, newValue, context, trackingNode);
                yield newValue;
            }
            default -> throw JsErrorException.typeError("cannot apply compound assignment to: " + node);
        };
    }

    /**
     * ES2021 logical-assignment (||=, &&=, ??=) with short-circuit semantics.
     * The LHS reference is resolved once (target + key are evaluated in spec
     * order, so `base[key()] ||= rhs` calls `key()` exactly once); the RHS
     * expression is evaluated only when the operator's condition requires it.
     */
    static Object logicalCompound(Node node, CoreContext context, TokenType operator, Node rhsNode, Node trackingNode) {
                return switch (node.type) {
            case REF_EXPR -> {
                int slot = node.slot;
                Object[] frame;
                if (slot >= 0 && (frame = context.frame) != null && frame[slot] != SlotTable.UNDECLARED) {
                    Object oldValue = frame[slot];
                    if (oldValue == SlotTable.TDZ) {
                        throw SlotTable.tdzError(node.getText());

View on GitHub (pinned to a22eb90246)