antlr/antlr4 · error · IllegalArgumentException

expected {} to be prefixed with {}

Error message

expected {} to be prefixed with {}

What it means

After counts balance, split(pattern) pairs each start with its stop in order and requires starts.get(i) < stops.get(i). A start delimiter appearing at or after its paired stop (e.g. '> <' with swapped custom delimiters) means the delimiters enclose nothing or are inverted, so 'tag delimiters out of order' IllegalArgumentException is thrown.

Source

Thrown at antlr4-maven-plugin/src/main/java/org/antlr/mojo/antlr4/MojoUtils.java:67

    }

    /**
     * Given the source directory File object and the full PATH to a grammar, produce the
     * path to the named grammar file in relative terms to the {@code sourceDirectory}.
     * This will then allow ANTLR to produce output relative to the base of the output
     * directory and reflect the input organization of the grammar files.
     *
     * @param   sourceDirectory  The source directory {@link File} object
     * @param   grammarFileName  The full path to the input grammar file
     *
     * @return  The path to the grammar file relative to the source directory
     */
    public static String findSourceSubdir(File sourceDirectory, File grammarFile) {
        String srcPath = sourceDirectory.getPath() + File.separator;
        String path = grammarFile.getPath();

        if (!path.startsWith(srcPath)) {
            throw new IllegalArgumentException("expected " + path +
                " to be prefixed with " + sourceDirectory);
        }

        File unprefixedGrammarFileName = new File(path.substring(srcPath.length()));

        if (unprefixedGrammarFileName.getParent() == null) {
            return "";
        }

        return unprefixedGrammarFileName.getParent() + File.separator;
    }
}

View on GitHub (pinned to 7d5770395b)

Solutions

  1. Use distinct start and stop delimiters with start occurring before stop in each tag.
  2. Avoid identical start/stop delimiter strings.
  3. Re-inspect patterns after changing delimiters via setDelimiters.

Example fix

// before
matcher.setDelimiters("%", "%", "\\"); // same delimiter -> ordering breaks
m.compile("%id%", R.expr);

// after
matcher.setDelimiters("<<", ">>", "\\");
m.compile("<<id>>", R.expr);
Defensive patterns

Strategy: try-catch

Validate before calling

// with custom delimiters, verify pairing order before compiling
List<Integer> s = indexOfAll(pattern, startDelim), t = indexOfAll(pattern, stopDelim);
boolean ordered = s.size() == t.size() && IntStream.range(0, s.size()).allMatch(i -> s.get(i) < t.get(i));
if (!ordered) throw new IllegalArgumentException("tag delimiters out of order: " + pattern);

Try / catch

try {
    pattern = matcher.compile(patternText, ruleIndex);
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("tag delimiters out of order")) {
        reportDelimiterMisconfiguration();
    }
    throw e;
}

Prevention

When it happens

Trigger: Custom delimiters where start and stop are swapped or identical (setDelimiters("%", "%")); patterns where an empty tag '<>' degenerates so the computed start offset is >= stop offset after ordering.

Common situations: Choosing symmetric custom delimiters; converting existing patterns when delimiters change and the ordering assumptions silently break.

Related errors


AI-assisted analysis of antlr/antlr4@7d5770395b (2026-08-14). Data as JSON: /api/errors/35e36d155c39bb7d. Report an issue: GitHub.