antlr/antlr4 · error · LexerNoViableAltException
LexerNoViableAltException('{}')
Error message
LexerNoViableAltException('{}') What it means
LexerATNSimulator.failOrAccept() raises LexerNoViableAltException when the DFA could not reach any accept state for the input from startIndex (and the input is not the immediate-EOF case, which returns Token.EOF). It is the lexer-side analogue of a syntax error: no lexer rule alternative matches the remaining characters, so no token can be produced.
Source
Thrown at runtime/Python3/src/antlr4/atn/LexerATNSimulator.py:254
# cause a failover from DFA later.
self. addDFAEdge(s, t, self.ERROR)
# stop when we can't match any more char
return self.ERROR
# Add an edge from s to target DFA found/created for reach
return self.addDFAEdge(s, t, cfgs=reach)
def failOrAccept(self, prevAccept:SimState , input:InputStream, reach:ATNConfigSet, t:int):
if self.prevAccept.dfaState is not None:
lexerActionExecutor = prevAccept.dfaState.lexerActionExecutor
self.accept(input, lexerActionExecutor, self.startIndex, prevAccept.index, prevAccept.line, prevAccept.column)
return prevAccept.dfaState.prediction
else:
# if no accept and EOF is first char, return EOF
if t==Token.EOF and input.index==self.startIndex:
return Token.EOF
raise LexerNoViableAltException(self.recog, input, self.startIndex, reach)
# Given a starting configuration set, figure out all ATN configurations
# we can reach upon input {@code t}. Parameter {@code reach} is a return
# parameter.
def getReachableConfigSet(self, input:InputStream, closure:ATNConfigSet, reach:ATNConfigSet, t:int):
# this is used to skip processing for configs which have a lower priority
# than a config that already reached an accept state for the same rule
skipAlt = ATN.INVALID_ALT_NUMBER
for cfg in closure:
currentAltReachedAcceptState = ( cfg.alt == skipAlt )
if currentAltReachedAcceptState and cfg.passedThroughNonGreedyDecision:
continue
if LexerATNSimulator.debug:
print("testing", self.getTokenName(t), "at", str(cfg))
for trans in cfg.state.transitions: # for each transition
target = self.getReachableTarget(trans, t)View on GitHub (pinned to 7d5770395b)
Solutions
- Add a catch-all lexer rule (e.g. UNEXPECTED: . ;) or explicitly enumerate the stray characters so the lexer can always produce a token, then handle it in the parser/listener.
- Attach a custom error listener (lexer.removeErrorListeners(); lexer.addErrorListener(...)) to report line/column instead of crashing.
- For mode grammars, audit pushMode/popMode pairing so each mode covers all inputs it can receive.
- Sanitize/validate input encoding (strip BOM, normalize unicode punctuation) before lexing.
Example fix
// before (grammar) ID: [a-zA-Z]+ ; INT: [0-9]+ ; WS: [ \t\r\n]+ -> skip ; // '$' -> LexerNoViableAltException // after (grammar) ID: [a-zA-Z]+ ; INT: [0-9]+ ; WS: [ \t\r\n]+ -> skip ; UNEXPECTED: . ; // always matches one char; report in parser/listener
Defensive patterns
Strategy: try-catch
Validate before calling
# Grammar-level guard: guarantee the lexer can always match something
# UNEXPECTED: . ; // add last in the lexer grammar
# Code-level guard: pre-scan input characters against a coverage set
allowed = set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 \t\r\n')
if any(ch not in allowed for ch in text):
raise ValueError('input contains characters the lexer grammar cannot match') Try / catch
from antlr4.error.Errors import LexerNoViableAltException
class CollectingErrorListener(ErrorListener):
def syntaxError(self, recognizer, offendingSymbol, line, column, msg, e):
errors.append((line, column, msg))
lexer.removeErrorListeners()
lexer.addErrorListener(CollectingErrorListener())
tokens = lexer # errors collected instead of raising; inspect lexer.symbolicNames for the offending text Prevention
- Add a trailing catch-all lexer rule (UNEXPECTED: . ;) so lexing never fails outright
- Install a custom error listener to capture line/column instead of exceptions
- Audit pushMode/popMode pairing in multi-mode grammars
- Strip BOMs and normalize exotic unicode before lexing
When it happens
Trigger: Input at the error position matches no lexer rule — an unrecognized character or sequence (e.g. '$' when the grammar has no rule covering it), or a mode-specific grammar receiving input not covered in the current lexer mode.
Common situations: Missing a catch-all rule like UNRECOGNIZED: . ; in the lexer; switching lexer modes (pushMode) and hitting characters only valid in another mode; UTF-8/BOM bytes or smart quotes in input when the grammar only covers ASCII; feeding the parser's lexer a stream of a different file type.
Related errors
- Empty Stack
- The specified lexer action type {} is not valid.
- Precedence predicates are not supported in lexers.
- nextToken requires a non-null input stream.
- replace: range invalid: {}..{}(size={})
AI-assisted analysis of antlr/antlr4@7d5770395b (2026-08-14).
Data as JSON: /api/errors/fde1d6ef09347e41.
Report an issue: GitHub.