karatelabs/karate · error · ParserException

label already in scope

Error message

label already in scope: ${name}

What it means

A statement label must be unique within its scope; re-declaring the same label in nested (non-function) code is a static error. labelNodeChecks maintains a linked Label chain and rejects a LABELLED_STMT whose name already appears in the chain. Function bodies, arrow functions, and class static blocks reset the chain (labels outside are invisible, so re-use there is fine).

Solutions

  1. Rename the inner (or outer) label to a unique name
  2. Move the inner labeled statement into its own function, which resets label scope
  3. Remove the redundant inner label if `break`/`continue` there can target the outer label legally

Example fix

// before
outer: for (;;) { outer: for (;;) { break outer; } }
// after
outer: for (;;) { inner: for (;;) { break inner; } }
Defensive patterns

Strategy: validation

Validate before calling

// Collect labels and flag re-declarations within one function scope:
const labels = [...src.matchAll(/([A-Za-z_$][\w$]*):\s*(?:for|while|do|\{|switch|if)/g)].map(m=>m[1]);
const dup = labels.filter((l,i)=>labels.indexOf(l)!==i);

Try / catch

try { eval(js); } catch (e) { if (String(e).includes('label already in scope')) { /* rename the inner label */ } throw e; }

Prevention

When it happens

Trigger: Writing `outer: ... outer: ...` where the second `outer` is nested within the first's statement (not separated by a function boundary) — Label.contains(labels, name, false) finds the prior binding.

Common situations: Copy-pasted loop blocks each carrying the same label inside one function; wrapping existing labeled code in another labeled block; template-generated loops reusing label names.

Related errors


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

Appendix: source

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

            }
            return false;
        }
    }

    /**
     * §14.13.1 label early errors at a single node (no recursion — {@link #earlyErrors}
     * drives the traversal): a label may not be nested inside a statement already carrying
     * it, {@code break} / {@code continue} may only name an enclosing label, and
     * {@code continue} additionally needs that label to name an iteration statement.
     * Returns the label chain visible to this node's children — nested function bodies see
     * none of them, since labels do not cross a function boundary.
     */
    private Label labelNodeChecks(Node node, Label labels) {
        switch (node.type) {
            case LABELLED_STMT -> {
                String name = node.getFirst().getText();
                if (Label.contains(labels, name, false)) {
                    throw new ParserException("label already in scope: " + name);
                }
                return new Label(name, labelsIterationStatement(node), labels);
            }
            // §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)) {

View on GitHub (pinned to a22eb90246)