{"record":{"id":"3e884289a0bcd1e7","repo":"antlr/antlr4","slug":"the-atn-must-be-a-lexer-atn-3e8842","errorCode":null,"errorMessage":"The ATN must be a lexer ATN.","messagePattern":"The ATN must be a lexer ATN\\.","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"runtime/CSharp/src/LexerInterpreter.cs","lineNumber":45,"sourceCode":"\n        [NotNull]\n        private readonly IVocabulary vocabulary;\n\n        protected DFA[] decisionToDFA;\n        protected PredictionContextCache sharedContextCache = new PredictionContextCache();\n\n        [Obsolete(\"Use constructor with channelNames argument\")]\n        public LexerInterpreter(string grammarFileName, IVocabulary vocabulary, IEnumerable<string> ruleNames, IEnumerable<string> modeNames, ATN atn, ICharStream input)\n            : this(grammarFileName, vocabulary, ruleNames, Collections.EmptyList<string>(), modeNames, atn, input)\n        {\n        }\n\n        public LexerInterpreter(string grammarFileName, IVocabulary vocabulary, IEnumerable<string> ruleNames, IEnumerable<string> channelNames, IEnumerable<string> modeNames, ATN atn, ICharStream input)\n            : base(input)\n        {\n            if (atn.grammarType != ATNType.Lexer)\n            {\n                throw new ArgumentException(\"The ATN must be a lexer ATN.\");\n            }\n            this.grammarFileName = grammarFileName;\n            this.atn = atn;\n            this.ruleNames = ruleNames.ToArray();\n            this.channelNames = channelNames.ToArray();\n            this.modeNames = modeNames.ToArray();\n            this.vocabulary = vocabulary;\n            this.decisionToDFA = new DFA[atn.NumberOfDecisions];\n            for (int i = 0; i < decisionToDFA.Length; i++)\n            {\n                decisionToDFA[i] = new DFA(atn.GetDecisionState(i), i);\n            }\n            this.Interpreter = new LexerATNSimulator(this, atn, decisionToDFA, sharedContextCache);\n        }\n\n        public override ATN Atn\n        {\n            get","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/antlr/antlr4/blob/7d5770395bb7b02eb56e7c62662cb1d7c08f42a3/runtime/CSharp/src/LexerInterpreter.cs#L27-L63","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nvar li = new LexerInterpreter(g.name, g.vocab, g.ruleNames, g.modeNames, g.atn, input); // g.atn is the parser ATN\n\n// after\nvar atn = new ATNDeserializer().Deserialize(g.atn);\nif (atn.grammarType != ATNType.Lexer)\n    throw new ArgumentException($\"{g.name}: expected lexer ATN, got {atn.grammarType}\");\nvar li = new LexerInterpreter(g.name, g.vocab, g.ruleNames, g.modeNames, atn, input);","handlingStrategy":"validation","validationCode":"if (atn.grammarType != ATNType.Lexer)\n    throw new ArgumentException($\"Expected lexer ATN, got {atn.grammarType}\", nameof(atn));","typeGuard":"static bool IsLexerAtn(ATN atn) => atn != null && atn.grammarType == ATNType.Lexer;","tryCatchPattern":null,"preventionTips":["Store grammarType metadata next to any persisted serialized ATN and verify on load","Keep parser and lexer ATN references in distinct, clearly named fields"],"tags":["antlr","csharp","lexer","interpreter","validation"],"backgroundTag":null,"analyzedSha":"7d5770395bb7b02eb56e7c62662cb1d7c08f42a3","analyzedAt":"2026-08-14T14:47:56.354Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}