antlr/antlr4 · error · ArgumentException

Unknown rule in pattern

Error message

Unknown rule {tag} in pattern: {pattern}

What it means

While compiling a pattern, a tag starting with a lowercase letter (rule tag, e.g. <expr>) does not resolve to any rule index in the parser (GetRuleIndex returned -1). Rule tags must name a parser rule declared in the grammar the parser was generated from; unknown names indicate a typo or a grammar/parser version mismatch.

Solutions

  1. Rename the tag to the exact rule name from the grammar (check parser.RuleNames or the generated parser class).
  2. If the tag names a token, capitalize its first letter so it is treated as a token tag.
  3. Regenerate the parser after grammar edits and update pattern strings together; add a startup check that compiles all patterns to fail fast.
  4. Wrap CompileParseTreePattern in try/catch ArgumentException to log which pattern failed when patterns come from external config.

Example fix

// before
var p = parser.CompileParseTreePattern("<ID> = <expression>", ExprParser.RULE_assign, null);

// after (rule is 'expr' in the grammar)
var p = parser.CompileParseTreePattern("<ID> = <expr>", ExprParser.RULE_assign, null);
Defensive patterns

Strategy: validation

Validate before calling

foreach (var tag in ExtractRuleTags(pattern))
    if (parser.GetRuleIndex(tag) == -1)
        throw new ArgumentException($"pattern references unknown rule <{tag}>");
var p = parser.CompileParseTreePattern(pattern, ruleIndex, null);

Try / catch

try { var p = parser.CompileParseTreePattern(pattern, ruleIndex, null); } catch (ArgumentException ex) when (ex.Message.StartsWith("Unknown rule")) { /* report pattern and parser.RuleNames */ }

Prevention

When it happens

Trigger: CompileParseTreePattern("<ID> = <expression>", ...) when the rule is called expr; using a pattern authored for a different grammar; rule renamed during refactoring without regenerating or updating patterns.

Common situations: Patterns stored as string constants drift after grammar refactors; multi-version tooling where the same pattern is run against parsers from several grammar versions; documentation examples copied into a project with different rule names.

Related errors


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

Appendix: source

Thrown at runtime/CSharp/src/Tree/Pattern/ParseTreePatternMatcher.cs:552

                    // add special rule token or conjure up new token from name
                    if (System.Char.IsUpper(tagChunk.Tag[0]))
                    {
                        int ttype = parser.GetTokenType(tagChunk.Tag);
                        if (ttype == TokenConstants.InvalidType)
                        {
                            throw new ArgumentException("Unknown token " + tagChunk.Tag + " in pattern: " + pattern);
                        }
                        TokenTagToken t = new TokenTagToken(tagChunk.Tag, ttype, tagChunk.Label);
                        tokens.Add(t);
                    }
                    else
                    {
                        if (System.Char.IsLower(tagChunk.Tag[0]))
                        {
                            int ruleIndex = parser.GetRuleIndex(tagChunk.Tag);
                            if (ruleIndex == -1)
                            {
                                throw new ArgumentException("Unknown rule " + tagChunk.Tag + " in pattern: " + pattern);
                            }
                            int ruleImaginaryTokenType = parser.GetATNWithBypassAlts().ruleToTokenType[ruleIndex];
                            tokens.Add(new RuleTagToken(tagChunk.Tag, ruleImaginaryTokenType, tagChunk.Label));
                        }
                        else
                        {
                            throw new ArgumentException("invalid tag: " + tagChunk.Tag + " in pattern: " + pattern);
                        }
                    }
                }
                else
                {
                    TextChunk textChunk = (TextChunk)chunk;
                    AntlrInputStream @in = new AntlrInputStream(textChunk.Text);
                    lexer.SetInputStream(@in);
                    IToken t = lexer.NextToken();
                    while (t.Type != TokenConstants.EOF)
                    {

View on GitHub (pinned to 7d5770395b)