antlr/antlr4 · error · Exception

%s at index %d isn't a valid rule name

Error message

%s at index %d isn't a valid rule name

What it means

Raised by XPath.getXPathElement() in the Python3 runtime when a RULE_REF in the path does not name any parser rule. The evaluator needs the rule index to match rule-invocation nodes; parser.ruleNames.index() returns -1 for unknown names.

Source

Thrown at runtime/Python3/src/antlr4/xpath/XPath.py:160

            tsource = self.parser.getTokenStream().tokenSource

            ttype = Token.INVALID_TYPE
            if wordToken.type == XPathLexer.TOKEN_REF:
                if word in tsource.ruleNames:
                    ttype = tsource.ruleNames.index(word) + 1
            else:
                if word in tsource.literalNames:
                    ttype = tsource.literalNames.index(word)

            if ttype == Token.INVALID_TYPE:
                raise Exception("%s at index %d isn't a valid token name" % (word, wordToken.tokenIndex))
            return XPathTokenAnywhereElement(word, ttype) if anywhere else XPathTokenElement(word, ttype)

        else:
            ruleIndex = self.parser.ruleNames.index(word) if word in self.parser.ruleNames else -1

            if ruleIndex == -1:
                raise Exception("%s at index %d isn't a valid rule name" % (word, wordToken.tokenIndex))
            return XPathRuleAnywhereElement(word, ruleIndex) if anywhere else XPathRuleElement(word, ruleIndex)


    @staticmethod
    def findAll(tree:ParseTree, xpath:str, parser:Parser):
        p = XPath(parser, xpath)
        return p.evaluate(tree)

    #
    # Return a list of all nodes starting at {@code t} as root that satisfy the
    # path. The root {@code /} is relative to the node passed to
    # {@link #evaluate}.
    #
    def evaluate(self, t:ParseTree):
        dummyRoot = ParserRuleContext()
        dummyRoot.children = [t] # don't set t's parent.

        work = [dummyRoot]

View on GitHub (pinned to 7d5770395b)

Solutions

  1. Match the rule name exactly to an entry in parser.ruleNames
  2. If the name is a token/lexer rule, reference it as a token instead
  3. Update XPath strings after every grammar rename and regenerate

Example fix

# before: rule is 'expression' in this grammar
nodes = XPath.findAll(tree, "/expr", parser)

# after
nodes = XPath.findAll(tree, "/expression", parser)
Defensive patterns

Strategy: validation

Validate before calling

if rule_name not in parser.ruleNames:
    raise ValueError(f'{rule_name} is not a parser rule name')

Type guard

def is_known_rule_name(name: str, parser) -> bool:
    return name in parser.ruleNames

Try / catch

try:
    nodes = XPath.findAll(tree, xpath, parser)
except Exception as e:
    if "isn't a valid rule name" in str(e):
        # find the correct rule name in parser.ruleNames
        ...

Prevention

When it happens

Trigger: XPath with a path like '/expr' when the parser has no rule named expr (e.g. it was renamed to expression), or using a lexer rule name in rule position.

Common situations: Grammar refactors, copy/pasting XPath examples from another grammar, or confusion between lexer and parser rule namespaces.

Related errors


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