karatelabs/karate · error · ParserException

continue label does not name a loop

Error message

continue label does not name a loop: ${target}

What it means

`continue` may only target labels that name iteration statements (loops). After confirming the label exists in scope, labelNodeChecks re-checks with the iteration flag (Label.contains(labels, target, true)); if the label names a non-loop labeled statement (e.g. a labeled block or switch), this early error is thrown.

Solutions

  1. Change `continue label;` to `break label;` if the target is a non-loop labeled statement and you want to exit it
  2. Retarget continue to a label that names an enclosing loop
  3. Restructure the non-loop labeled statement into a loop if iteration semantics are intended

Example fix

// before
blk: { continue blk; }
// after
blk: { break blk; }
Defensive patterns

Strategy: validation

Validate before calling

// Verify continue targets label loops, not blocks/switch:
const cont = [...src.matchAll(/continue\s+([A-Za-z_$][\w$]*)/g)].map(m=>m[1]);
const loopLabels = new Set([...src.matchAll(/([A-Za-z_$][\w$]*):\s*(?:for|while|do)\b/g)].map(m=>m[1]));
const bad = cont.filter(t => !loopLabels.has(t));

Try / catch

try { eval(js); } catch (e) { if (String(e).includes('continue label does not name a loop')) { /* switch to `break label` or retarget a loop label */ } throw e; }

Prevention

When it happens

Trigger: `continue blockLabel;` where blockLabel labels a plain block `{ }`, an `if`, or a `switch` rather than a for/while/do loop — e.g. `blk: { continue blk; }`.

Common situations: Changing a loop into a labeled block while keeping a continue pointing at it; misunderstanding that `continue` (unlike `break`) cannot target arbitrary labeled statements.

Related errors


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

Appendix: source

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

            // §15.7.1 makes a static initialization block a boundary of the same kind
            // as a function body: nothing inside it may name a label outside it.
            case FN_EXPR, FN_ARROW_EXPR, CLASS_STATIC_BLOCK -> {
                return null;
            }
            case BREAK_STMT -> {
                String target = labelReference(node);
                if (target != null && !Label.contains(labels, target, false)) {
                    throw new ParserException("break references an undefined label: " + target);
                }
            }
            case CONTINUE_STMT -> {
                String target = labelReference(node);
                if (target != null) {
                    if (!Label.contains(labels, target, false)) {
                        throw new ParserException("continue references an undefined label: " + target);
                    }
                    if (!Label.contains(labels, target, true)) {
                        throw new ParserException("continue label does not name a loop: " + target);
                    }
                }
            }
            default -> {
            }
        }
        return labels;
    }

    /** The label named by a {@code break} / {@code continue}, or null when it has none —
     *  children are [keyword] or [keyword, label]. */
    private static String labelReference(Node node) {
        return node.size() > 1 ? node.get(1).getText() : null;
    }

    /** True if the labelled item — after peeling any stacked labels, since {@code a: b: for}
     *  makes both names loop labels — is an iteration statement. */
    private static boolean labelsIterationStatement(Node labelled) {

View on GitHub (pinned to a22eb90246)