karatelabs/karate · error · ParserException

a class declaration may not be the body of

Error message

a class declaration may not be the body of ${where}

What it means

A class declaration, like a function declaration, is a Declaration rather than a Statement, so it cannot be the unbraced body of `if`/`else`, a loop, or a labelled clause. Karate's parser rejects this unconditionally (there is no sloppy-mode carve-out for classes). The body must be a braced block.

Solutions

  1. Brace the clause: `if (cond) { class C {} }`.
  2. Declare the class outside the clause and reference it inside.
  3. Use a class expression: `if (cond) { const C = class {}; }`.

Example fix

// before
if (flag) class Point { constructor(x) { this.x = x; } }
// after
if (flag) { class Point { constructor(x) { this.x = x; } } }
Defensive patterns

Strategy: validation

Validate before calling

function hasUnbracedClassBody(src) {
  return /(^|[^\w$])(if|else|for|while|do|\w+:)\s*class\s*[\w$]*/.test(src);
}
if (hasUnbracedClassBody(src)) throw new Error('brace the class declaration body');

Try / catch

try {
  karate.eval(script);
} catch (e) {
  if (String(e.message).startsWith('a class declaration may not be the body of')) {
    // rewrite the clause with braces and retry
  } else throw e;
}

Prevention

When it happens

Trigger: Writing `if (cond) class C {}`, `while (x) class C {}`, or `label: class C {}` in Karate-embedded JS.

Common situations: Authors omitting braces around multi-token declarations; code ported from a transpiler that assumed lenient parsing; teaching examples written sloppily.

Related errors


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

Appendix: source

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

    }

    /** Throws if any direct {@code STATEMENT} child of {@code node} is itself a
     *  LexicalDeclaration ({@code let}/{@code const}) or a ClassDeclaration — the body
     *  of an `if`/`else`/loop clause is a Statement, and both are Declarations, not
     *  Statements (§13.6/§14.x). A {@code var} declaration hoists and stays legal; a
     *  braced body (BLOCK) is fine. Mode-independent — there is no Annex B carve-out
     *  for these the way there is for FunctionDeclaration in an `if` clause. */
    private static void checkNoLexicalOrClassDeclarationBody(Node node, String where) {
        for (int i = 0, n = node.size(); i < n; i++) {
            Node child = node.get(i);
            if (child.isToken() || child.type != NodeType.STATEMENT) {
                continue;
            }
            for (int j = 0, m = child.size(); j < m; j++) {
                Node inner = child.get(j);
                if (!inner.isToken()) {
                    if (inner.type == NodeType.CLASS_EXPR) {
                        throw new ParserException(
                                "a class declaration may not be the body of " + where);
                    }
                    if (inner.type == NodeType.VAR_STMT && isLexicalVarStmt(inner)) {
                        throw new ParserException(
                                "a lexical declaration may not be the body of " + where);
                    }
                    break; // first non-token child decides
                }
            }
        }
    }

    /** True if a {@code VAR_STMT}'s leading keyword token is {@code let} / {@code const}
     *  (a LexicalDeclaration) rather than {@code var}. The keyword distinction lives on
     *  VAR_STMT, not on the VAR_DECL children.
     *  <p>One sloppy-mode subtlety: {@code let} is not a reserved word, so as the body of
     *  an `if`/loop clause {@code let} followed by a LineTerminator is the identifier
     *  {@code let} as an ExpressionStatement with ASI inserted before the next line

View on GitHub (pinned to a22eb90246)