karatelabs/karate · error · ParserException

'break' may not cross a class static initialization block

Error message

'break' may not cross a class static initialization block

What it means

An unlabeled `break` inside a class static initialization block has no enclosing loop or switch to target (the block does not count), so it is an early error. The Karate JS parser throws this when a bare BREAK_STMT (no label) is found in a static block outside any nested function, loop, or switch (SB_FN | SB_LOOP | SB_SWITCH flags unset).

Solutions

  1. Remove the stray `break` and restructure the condition (`if (cond) { ... }` around the rest).
  2. Wrap the breakable logic in an actual loop or switch, or in a nested function using `return`.
  3. If exiting early from a labeled construct, ensure the label refers to a loop/switch inside the block.

Example fix

// before
class C { static { for (const x of xs) { if (x.bad) break; use(x); } } } // OK
// before (invalid)
class C { static { if (!ready) break; init(); } }
// after
class C { static { if (ready) init(); } }
Defensive patterns

Strategy: validation

Validate before calling

// flag unlabeled break in static block source outside loops/switches (naive scan)
function noBareBreak(src) { return !/(^|[{;\s])break\s*[;}]/.test(src); }

Try / catch

try { karate.eval(blockSrc); } catch (e) { if (String(e).includes("'break' may not cross a class static initialization block")) { /* restructure control flow */ } }

Prevention

When it happens

Trigger: Parsing `class C { static { break; } }` or any unlabeled `break` in a static block not inside a nested `for`/`while`/`switch`/function.

Common situations: Copy-pasting loop-exit code into a static initializer; replacing `return` with `break` after hitting the return restriction; breaking out of a block body expecting C-style `break` semantics.

Related errors


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

Appendix: source

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

                // `x.await` / `x?.await` name a member, they do not reference one
                if (keepAwait && type == IDENT && prev != DOT && prev != QUES_DOT
                        && isIdentText(child.token, "await")) {
                    throw new ParserException("'await' is a reserved word in a class static initialization block");
                }
                prev = type;
                continue;
            }
            prev = null;
            int childFlags = flags;
            switch (child.type) {
                case RETURN_STMT -> {
                    if ((flags & SB_FN) == 0) {
                        throw new ParserException("'return' is not allowed in a class static initialization block");
                    }
                }
                case BREAK_STMT -> {
                    if ((flags & (SB_FN | SB_LOOP | SB_SWITCH)) == 0 && child.size() == 1) {
                        throw new ParserException("'break' may not cross a class static initialization block");
                    }
                }
                case CONTINUE_STMT -> {
                    if ((flags & (SB_FN | SB_LOOP)) == 0 && child.size() == 1) {
                        throw new ParserException("'continue' may not cross a class static initialization block");
                    }
                }
                case FOR_STMT, WHILE_STMT, DO_WHILE_STMT -> childFlags |= SB_LOOP;
                case SWITCH_STMT -> childFlags |= SB_SWITCH;
                case FN_EXPR, FN_ARROW_EXPR -> childFlags |= SB_FN;
                default -> {
                }
            }
            // A function's parameters are [~Await] along with its body; an arrow's are
            // not — only the body (always its last child) leaves the reserved region.
            if (node.type == NodeType.FN_EXPR
                    || (node.type == NodeType.FN_ARROW_EXPR && i == last)) {
                childFlags |= SB_AWAIT_OK;

View on GitHub (pinned to a22eb90246)