antlr/antlr4 · error · ArgumentException

invalid tag: in pattern

Error message

invalid tag: {tag} in pattern: {pattern}

What it means

While compiling a pattern, a tag's first character is neither uppercase (token tag) nor lowercase (rule tag) — e.g. it starts with a digit, underscore, or non-ASCII character. The matcher's convention is: uppercase first letter = token reference, lowercase first letter = rule reference; anything else cannot be classified and is rejected.

Solutions

  1. Rename the tag so its first letter is uppercase (token) or lowercase (rule) per ANTLR's convention.
  2. Use labels for arbitrary names: <name:expr> — the label part is unrestricted, only the tag itself must start with a letter.
  3. Sanitize programmatically generated tags: ensure the first char is an ASCII letter before embedding them in a pattern.

Example fix

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

// after
var p = parser.CompileParseTreePattern("<val:_id> = <expr>", ExprParser.RULE_assign, null); // or rename tag to 'id'
Defensive patterns

Strategy: validation

Validate before calling

static bool IsValidTagFirstChar(char c) => char.IsUpper(c) || char.IsLower(c);
// sanitize generated tags
string tag = char.IsLetter(raw[0]) ? raw : "x" + raw;

Type guard

static bool IsValidTag(string tag) => tag.Length > 0 && (char.IsUpper(tag[0]) || char.IsLower(tag[0]));

Prevention

When it happens

Trigger: A pattern containing a tag like <_id>, <2nd>, <Ünicode>, or <'quoted'> passed to CompileParseTreePattern or ParseTreePatternMatcher.Compile.

Common situations: Programmatically building pattern strings and interpolating user input that starts with a digit; using snake_case tag names (common in C# conventions) inside patterns; localized grammars with non-ASCII identifiers.

Related errors


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

Appendix: source

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

                        }
                        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)
                    {
                        tokens.Add(t);
                        t = lexer.NextToken();
                    }
                }
            }
            //		System.out.println("tokens="+tokens);
            return tokens;

View on GitHub (pinned to 7d5770395b)