karatelabs/karate · error · JsErrorException (typeError)

cannot apply post inc/dec to

Error message

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

What it means

A TypeError thrown when a post-increment/decrement operator is applied to a node type that is not a variable reference or property access. Literal and other unexpected node shapes reach the switch's default arm; literals themselves are handled as no-ops ((x)++ returns the value unchanged), so this error signals a truly unsupported LHS form.

Solutions

  1. Rewrite so the increment target is a variable or property: `obj.count++`
  2. Assign the expression result to a variable first, then increment the variable
  3. Check for parser/interpreter version mismatches if the source looks valid

Example fix

// before
(a + b)++;
// after
let s = a + b;
s++;
Defensive patterns

Strategy: validation

Validate before calling

// Reject increment/decrement on non-assignable expressions
if (/\+\+\s*[^A-Za-z_$([]|--[^A-Za-z_$([]/.test(code)) { /* flag invalid target */ }

Type guard

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

Prevention

When it happens

Trigger: Executing `expr++` where expr parses as neither REF_EXPR, REF_DOT_EXPR, REF_BRACKET_EXPR, nor LIT_EXPR — e.g. `(a, b)++`, assignment to a call, or a malformed AST from a parser bug.

Common situations: Generated or transpiled code emitting invalid increment targets; hand-written JS like `foo()++`; engine version mismatch between parser and 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/76c72af8da96dcc4. Report an issue: GitHub.

Appendix: source

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

                    yield oldValue;
                }
                yield incDecRefByName(node, context, isIncrement, false);
            }
            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, false, context);
                if (site.receiver != null) yield superIncDec(site, isIncrement, false, context);
                yield site.isIndex
                        ? postIncDecByIndex(site.target, site.key, isIncrement, context)
                        : postIncDecByName(site.target, (String) site.key, isIncrement, context);
            }
            case LIT_EXPR -> {
                // Handle literals like (x)++ where x is wrapped
                Object oldValue = Interpreter.eval(node, context);
                yield oldValue; // Can't actually modify a literal result
            }
            default -> throw JsErrorException.typeError("cannot apply post inc/dec to: " + node);
        };
    }

    /**
     * Pre-increment/decrement: updates variable, returns new value.
     */
    static Object preIncDec(Node node, CoreContext context, boolean isIncrement) {
                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());
                    }
                    Object step = Terms.incDecStep(oldValue);
                    Object newValue = isIncrement ? Terms.add(oldValue, step, context) : Terms.min(oldValue, step, context);

View on GitHub (pinned to a22eb90246)