antlr/antlr4 · critical · Exception
IllegalState
Error message
IllegalState
What it means
While linking deserialized states, every BlockStartState must reference a BlockEndState. This check fires when a BlockStartState has endState == None, i.e. the serialized ATN is internally inconsistent. With well-formed ANTLR tool output this never happens; in practice it signals truncated or corrupted serialized ATN data (e.g. a partial copy of a generated file, or a version skew that misaligns the data stream).
Source
Thrown at runtime/Python3/src/antlr4/atn/ATNDeserializer.py:167
# edges for rule stop states can be derived, so they aren't serialized
for state in atn.states:
for i in range(0, len(state.transitions)):
t = state.transitions[i]
if not isinstance(t, RuleTransition):
continue
outermostPrecedenceReturn = -1
if atn.ruleToStartState[t.target.ruleIndex].isPrecedenceRule:
if t.precedence == 0:
outermostPrecedenceReturn = t.target.ruleIndex
trans = EpsilonTransition(t.followState, outermostPrecedenceReturn)
atn.ruleToStopState[t.target.ruleIndex].addTransition(trans)
for state in atn.states:
if isinstance(state, BlockStartState):
# we need to know the end state to set its start state
if state.endState is None:
raise Exception("IllegalState")
# block end states can only be associated to a single block start state
if state.endState.startState is not None:
raise Exception("IllegalState")
state.endState.startState = state
if isinstance(state, PlusLoopbackState):
for i in range(0, len(state.transitions)):
target = state.transitions[i].target
if isinstance(target, PlusBlockStartState):
target.loopBackState = state
elif isinstance(state, StarLoopbackState):
for i in range(0, len(state.transitions)):
target = state.transitions[i].target
if isinstance(target, StarLoopEntryState):
target.loopBackState = state
def readDecisions(self, atn:ATN):
ndecisions = self.readInt()View on GitHub (pinned to 7d5770395b)
Solutions
- Regenerate the lexer/parser from the grammar with the ANTLR tool matching your runtime version and discard any manually patched generated files.
- Verify the generated file was not mangled (line endings, encoding, copy-paste truncation); restore from a clean generation.
- Confirm the runtime version equals the tool version (see error 'Could not deserialize ATN with version').
Defensive patterns
Strategy: try-catch
Try / catch
try:
parser = MyParser(tokens)
except Exception as e:
if 'IllegalState' in str(e):
raise RuntimeError('Serialized ATN is corrupt: regenerate generated files with the matching ANTLR tool version')
raise Prevention
- Never hand-edit the serialized ATN literal inside generated files
- Keep generated files byte-identical to fresh generation (diff in CI)
- Match tool and runtime versions
When it happens
Trigger: ATNDeserializer.deserialize() where a block start state's serialized end-state reference failed to resolve — corrupt data array, truncated serialized string, or deserialization reading garbage after a version mismatch.
Common situations: Hand-editing or regex-refactoring generated parser files and breaking the embedded serialized ATN; file transfer/encoding corruption (non-UTF-8 round trip); tool/runtime version mismatch shifting the read cursor.
Related errors
- Could not deserialize ATN with version {} (expected {}).
- Couldn't identify final state of the precedence rule prefix
- The specified transition type: {} is not valid.
- The specified state type {} is not valid.
- Invalid state number.
AI-assisted analysis of antlr/antlr4@7d5770395b (2026-08-14).
Data as JSON: /api/errors/e183ec4bf550ee5b.
Report an issue: GitHub.