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

  1. Regenerate the lexer/parser from the grammar with the ANTLR tool matching your runtime version and discard any manually patched generated files.
  2. Verify the generated file was not mangled (line endings, encoding, copy-paste truncation); restore from a clean generation.
  3. 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

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


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