{"record":{"id":"71b3de05318354ed","repo":"antlr/antlr4","slug":"invalid-tag-s-in-pattern-s","errorCode":null,"errorMessage":"invalid tag: %s in pattern: %s","messagePattern":"invalid tag: (.+?) in pattern: (.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"runtime/Python3/src/antlr4/tree/ParseTreePatternMatcher.py","lineNumber":296,"sourceCode":"\n        # create token stream from text and tags\n        tokens = list()\n        for chunk in chunks:\n            if isinstance( chunk, TagChunk ):\n                # add special rule token or conjure up new token from name\n                if chunk.tag[0].isupper():\n                    ttype = self.parser.getTokenType(chunk.tag)\n                    if ttype==Token.INVALID_TYPE:\n                        raise Exception(\"Unknown token \" + str(chunk.tag) + \" in pattern: \" + pattern)\n                    tokens.append(TokenTagToken(chunk.tag, ttype, chunk.label))\n                elif chunk.tag[0].islower():\n                    ruleIndex = self.parser.getRuleIndex(chunk.tag)\n                    if ruleIndex==-1:\n                        raise Exception(\"Unknown rule \" + str(chunk.tag) + \" in pattern: \" + pattern)\n                    ruleImaginaryTokenType = self.parser.getATNWithBypassAlts().ruleToTokenType[ruleIndex]\n                    tokens.append(RuleTagToken(chunk.tag, ruleImaginaryTokenType, chunk.label))\n                else:\n                    raise Exception(\"invalid tag: \" + str(chunk.tag) + \" in pattern: \" + pattern)\n            else:\n                self.lexer.setInputStream(InputStream(chunk.text))\n                t = self.lexer.nextToken()\n                while t.type!=Token.EOF:\n                    tokens.append(t)\n                    t = self.lexer.nextToken()\n        return tokens\n\n    # Split {@code <ID> = <e:expr> ;} into 4 chunks for tokenizing by {@link #tokenize}.#\n    def split(self, pattern:str):\n        p = 0\n        n = len(pattern)\n        chunks = list()\n        # find all start and stop indexes first, then collect\n        starts = list()\n        stops = list()\n        while p < n :\n            if p == pattern.find(self.escape + self.start, p):","sourceCodeStart":278,"sourceCodeEnd":314,"githubUrl":"https://github.com/antlr/antlr4/blob/7d5770395bb7b02eb56e7c62662cb1d7c08f42a3/runtime/Python3/src/antlr4/tree/ParseTreePatternMatcher.py#L278-L314","documentation":"Raised by ParseTreePatternMatcher.tokenize() when a tag's first character is neither uppercase nor lowercase, so the matcher cannot decide whether it is a token tag or a rule tag. ANTLR's convention is uppercase-first = token tag, lowercase-first = rule tag; digits, '_', or symbols as the first character are rejected.","triggerScenarios":"Compiling a pattern with a tag like <_id>, <9num>, <id:1x>, or an empty tag <> after delimiter handling. Unicode or punctuation-first tag names also trigger it.","commonSituations":"Using non-identifier tag names, or a mangled tag after misconfigured delimiters cause part of the text to be read as a tag.","solutions":["Rename the tag so it starts with a letter (uppercase for tokens, lowercase for rules)","Verify custom delimiters are set correctly so plain text is not mistaken for a tag","Escape literal delimiter characters with the escape sequence instead of letting them form tags"],"exampleFix":"# before\npattern = parser.compilePattern('<_value>', 0)\n\n# after\npattern = parser.compilePattern('<value>', 0)","handlingStrategy":"validation","validationCode":"import re\nassert re.match(r'^[A-Za-z]', tag), 'tag must start with a letter'","typeGuard":"def is_valid_tag(tag: str) -> bool:\n    return len(tag) > 0 and tag[0].isalpha()","tryCatchPattern":null,"preventionTips":["Start tags with a letter: uppercase for tokens, lowercase for rules","Keep delimiter configuration consistent"],"tags":["antlr","python","parse-tree-matching","pattern-compile"],"backgroundTag":null,"analyzedSha":"7d5770395bb7b02eb56e7c62662cb1d7c08f42a3","analyzedAt":"2026-08-14T14:47:56.354Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}