antlr/antlr4 · error · ArgumentException
The ATN must be a lexer ATN.
Error message
The ATN must be a lexer ATN.
What it means
LexerInterpreter executes a serialized lexer ATN directly (used for interpreted, non-generated lexing). Its constructor validates that the supplied ATN has grammarType == ATNType.Lexer, because everything downstream — LexerATNSimulator, mode handling, action dispatch — assumes lexer ATN structure. Supplying a parser ATN would fail much later and confusingly, so the constructor rejects it up front.
Source
Thrown at runtime/CSharp/src/LexerInterpreter.cs:45
[NotNull]
private readonly IVocabulary vocabulary;
protected DFA[] decisionToDFA;
protected PredictionContextCache sharedContextCache = new PredictionContextCache();
[Obsolete("Use constructor with channelNames argument")]
public LexerInterpreter(string grammarFileName, IVocabulary vocabulary, IEnumerable<string> ruleNames, IEnumerable<string> modeNames, ATN atn, ICharStream input)
: this(grammarFileName, vocabulary, ruleNames, Collections.EmptyList<string>(), modeNames, atn, input)
{
}
public LexerInterpreter(string grammarFileName, IVocabulary vocabulary, IEnumerable<string> ruleNames, IEnumerable<string> channelNames, IEnumerable<string> modeNames, ATN atn, ICharStream input)
: base(input)
{
if (atn.grammarType != ATNType.Lexer)
{
throw new ArgumentException("The ATN must be a lexer ATN.");
}
this.grammarFileName = grammarFileName;
this.atn = atn;
this.ruleNames = ruleNames.ToArray();
this.channelNames = channelNames.ToArray();
this.modeNames = modeNames.ToArray();
this.vocabulary = vocabulary;
this.decisionToDFA = new DFA[atn.NumberOfDecisions];
for (int i = 0; i < decisionToDFA.Length; i++)
{
decisionToDFA[i] = new DFA(atn.GetDecisionState(i), i);
}
this.Interpreter = new LexerATNSimulator(this, atn, decisionToDFA, sharedContextCache);
}
public override ATN Atn
{
getView on GitHub (pinned to 7d5770395b)
Solutions
- Check atn.grammarType == ATNType.Lexer before constructing LexerInterpreter (and == ATNType.Parser for ParserInterpreter)
- Store grammarType alongside serialized ATNs when you persist them, and validate on load
- In split grammars, keep the lexer and parser ATNs in separately named fields and never share variables between them
Example fix
// before
var li = new LexerInterpreter(g.name, g.vocab, g.ruleNames, g.modeNames, g.atn, input); // g.atn is the parser ATN
// after
var atn = new ATNDeserializer().Deserialize(g.atn);
if (atn.grammarType != ATNType.Lexer)
throw new ArgumentException($"{g.name}: expected lexer ATN, got {atn.grammarType}");
var li = new LexerInterpreter(g.name, g.vocab, g.ruleNames, g.modeNames, atn, input); Defensive patterns
Strategy: validation
Validate before calling
if (atn.grammarType != ATNType.Lexer)
throw new ArgumentException($"Expected lexer ATN, got {atn.grammarType}", nameof(atn)); Type guard
static bool IsLexerAtn(ATN atn) => atn != null && atn.grammarType == ATNType.Lexer;
Prevention
- Store grammarType metadata next to any persisted serialized ATN and verify on load
- Keep parser and lexer ATN references in distinct, clearly named fields
When it happens
Trigger: Passing an ATN deserialized from a parser's serialized ATN constant into LexerInterpreter, or sharing one ATN object between parser and lexer interpreter paths by mistake. Also grammar-type mixups when loading ATNs from files/registries by name.
Common situations: Dynamic grammar interpretation tools that hold ATNs for both a parser and a lexer and pass the wrong one; caching ATNs by grammar name where parser and lexer share a name (split grammar 'MyGrammar' lexer/parser); loading serialized ATNs from a config store without recording their type.
Related errors
- Invalid state number.
- The specified lexer action type {0} is not valid.
- Precedence predicates are not supported in lexers.
- nextToken requires a non-null input stream.
- start cannot be null or empty
AI-assisted analysis of antlr/antlr4@7d5770395b (2026-08-14).
Data as JSON: /api/errors/3e884289a0bcd1e7.
Report an issue: GitHub.