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.

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.

Appendix: 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)