karatelabs/karate · error · JsErrorException (typeError)

cannot apply pre inc/dec to

Error message

cannot apply pre inc/dec to: ${node}

What it means

A TypeError thrown when a pre-increment/decrement operator is applied to a node type the evaluator does not support as an update target. Valid targets are variable references, property accesses (with private/super/index/name handling), and literals are simply not reachable here; anything else hits the default arm.

Solutions

  1. Rewrite the target as a variable or property: `++obj.count`
  2. Store the expression in a variable, then apply `++` to that variable
  3. If the source is valid, check for a parser regression and upgrade

Example fix

// before
++(x + y);
// after
let s = x + y;
++s;
Defensive patterns

Strategy: validation

Validate before calling

// Reject pre-inc/dec on non-assignable expressions before eval
if (/\+\+\s*[(\d'"]|--\s*[(\d'"]/.test(code)) { /* invalid pre-inc/dec target */ }

Type guard

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

Prevention

When it happens

Trigger: Executing `++expr`/`--expr` where expr parses as an unsupported node shape — e.g. `++(a, b)`, `++foo()`, or a malformed AST node from a parser defect.

Common situations: Hand-written invalid JS like `++obj.getMethod()`; code generators or transpilers emitting bad increment targets; parser/interpreter version skew.

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

Appendix: source

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

                        throw SlotTable.tdzError(node.getText());
                    }
                    Object step = Terms.incDecStep(oldValue);
                    Object newValue = isIncrement ? Terms.add(oldValue, step, context) : Terms.min(oldValue, step, context);
                    context.updateSlot(slot, newValue, null);
                    yield newValue;
                }
                yield incDecRefByName(node, context, isIncrement, true);
            }
            case REF_DOT_EXPR, REF_BRACKET_EXPR -> {
                AccessSite site = resolveWriteSite(node, context);
                if (site == null || site == SHORT_CIRCUIT_SITE) yield Terms.UNDEFINED;
                if (site.privateName != null) yield privateIncDec(site, isIncrement, true, context);
                if (site.receiver != null) yield superIncDec(site, isIncrement, true, context);
                yield site.isIndex
                        ? preIncDecByIndex(site.target, site.key, isIncrement, context)
                        : preIncDecByName(site.target, (String) site.key, isIncrement, context);
            }
            default -> throw JsErrorException.typeError("cannot apply pre inc/dec to: " + node);
        };
    }

    /**
     * Delete a property. Returns true on success.
     */
    static boolean delete(Node node, CoreContext context) {
                return switch (node.type) {
            case REF_EXPR -> false; // Can't delete variables
            case REF_DOT_EXPR, REF_BRACKET_EXPR -> {
                AccessSite site;
                try {
                    site = resolveWriteSite(node, context);
                } catch (JsErrorException e) {
                    // delete on `?.()` shape: legacy behavior was to return false silently
                    yield false;
                }
                // a short-circuited chain leaves no reference to delete, which

View on GitHub (pinned to a22eb90246)