antlr/antlr4 · error · Exception

Unknown rule %s in pattern: %s

Error message

Unknown rule %s in pattern: %s

What it means

Raised by ParseTreePatternMatcher.tokenize() when a tag starting with a lowercase letter does not match any parser rule. Lowercase-first tags are rule tags (e.g. <expr>); the matcher resolves them via parser.getRuleIndex(), which returns -1 for unknown names.

Source

Thrown at runtime/Python3/src/antlr4/tree/ParseTreePatternMatcher.py:292

    def tokenize(self, pattern:str):
        # split pattern into chunks: sea (raw input) and islands (<ID>, <expr>)
        chunks = self.split(pattern)

        # create token stream from text and tags
        tokens = list()
        for chunk in chunks:
            if isinstance( chunk, TagChunk ):
                # add special rule token or conjure up new token from name
                if chunk.tag[0].isupper():
                    ttype = self.parser.getTokenType(chunk.tag)
                    if ttype==Token.INVALID_TYPE:
                        raise Exception("Unknown token " + str(chunk.tag) + " in pattern: " + pattern)
                    tokens.append(TokenTagToken(chunk.tag, ttype, chunk.label))
                elif chunk.tag[0].islower():
                    ruleIndex = self.parser.getRuleIndex(chunk.tag)
                    if ruleIndex==-1:
                        raise Exception("Unknown rule " + str(chunk.tag) + " in pattern: " + pattern)
                    ruleImaginaryTokenType = self.parser.getATNWithBypassAlts().ruleToTokenType[ruleIndex]
                    tokens.append(RuleTagToken(chunk.tag, ruleImaginaryTokenType, chunk.label))
                else:
                    raise Exception("invalid tag: " + str(chunk.tag) + " in pattern: " + pattern)
            else:
                self.lexer.setInputStream(InputStream(chunk.text))
                t = self.lexer.nextToken()
                while t.type!=Token.EOF:
                    tokens.append(t)
                    t = self.lexer.nextToken()
        return tokens

    # Split {@code <ID> = <e:expr> ;} into 4 chunks for tokenizing by {@link #tokenize}.#
    def split(self, pattern:str):
        p = 0
        n = len(pattern)
        chunks = list()
        # find all start and stop indexes first, then collect

View on GitHub (pinned to 7d5770395b)

Solutions

  1. Match the tag exactly to a rule name in parser.ruleNames
  2. Regenerate the parser from the current grammar and retry
  3. If you meant a token, capitalize the first letter (<ID>)

Example fix

# before: rule is 'expr' in the grammar
pattern = parser.compilePattern('<expression>', exprRuleIndex)

# after
pattern = parser.compilePattern('<expr>', exprRuleIndex)
Defensive patterns

Strategy: validation

Validate before calling

assert 'expr' in parser.ruleNames, 'unknown rule tag'
pattern = parser.compilePattern('<expr>', exprIdx)

Type guard

def is_known_rule(tag: str, parser) -> bool:
    return parser.getRuleIndex(tag) != -1

Try / catch

try:
    p = matcher.compileTreePattern(pat, idx)
except Exception as e:
    if str(e).startswith('Unknown rule'):
        # check parser.ruleNames for the correct name
        ...

Prevention

When it happens

Trigger: Compiling a pattern with a tag like <expression> when the grammar rule is named expr, or referencing a rule from a different grammar version.

Common situations: Grammar refactor renamed rules, pattern shared across grammars, or assuming a lexer rule name works as a parser rule tag.

Related errors


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