{"record":{"id":"be90bc28a0b69f8e","repo":"antlr/antlr4","slug":"couldn-t-identify-final-state-of-the-precedence-ru-be90bc","errorCode":null,"errorMessage":"Couldn't identify final state of the precedence rule prefix section.","messagePattern":"Couldn't identify final state of the precedence rule prefix section\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"critical","filePath":"runtime/Python3/src/antlr4/atn/ATNDeserializer.py","lineNumber":240,"sourceCode":"\n        bypassStart.endState = bypassStop\n        atn.defineDecisionState(bypassStart)\n\n        bypassStop.startState = bypassStart\n\n        excludeTransition = None\n\n        if atn.ruleToStartState[idx].isPrecedenceRule:\n            # wrap from the beginning of the rule to the StarLoopEntryState\n            endState = None\n            for state in atn.states:\n                if self.stateIsEndStateFor(state, idx):\n                    endState = state\n                    excludeTransition = state.loopBackState.transitions[0]\n                    break\n\n            if excludeTransition is None:\n                raise Exception(\"Couldn't identify final state of the precedence rule prefix section.\")\n\n        else:\n\n            endState = atn.ruleToStopState[idx]\n\n        # all non-excluded transitions that currently target end state need to target blockEnd instead\n        for state in atn.states:\n            for transition in state.transitions:\n                if transition == excludeTransition:\n                    continue\n                if transition.target == endState:\n                    transition.target = bypassStop\n\n        # all transitions leaving the rule start state need to leave blockStart instead\n        ruleToStartState = atn.ruleToStartState[idx]\n        count = len(ruleToStartState.transitions)\n        while count > 0:\n            bypassStart.addTransition(ruleToStartState.transitions[count-1])","sourceCodeStart":222,"sourceCodeEnd":258,"githubUrl":"https://github.com/antlr/antlr4/blob/7d5770395bb7b02eb56e7c62662cb1d7c08f42a3/runtime/Python3/src/antlr4/atn/ATNDeserializer.py#L222-L258","documentation":"When deserializing with generateRuleBypassTransitions enabled (needed for parse tree pattern matching, XPath, and the TokensRule bypass), the deserializer wraps each left-recursive rule by locating its StarLoopEntryState-derived end state. If no state in the ATN satisfies stateIsEndStateFor(state, ruleIndex) for a precedence rule, the wrapping cannot proceed and this Exception is raised — effectively a malformed-ATN error on the left-recursion machinery.","triggerScenarios":"ATNDeserializationOptions with generateRuleBypassTransitions=True deserializing an ATN whose precedence (left-recursive) rules lack a recognizable loop-back end state — corrupt data or an ATN produced by a mismatched tool version.","commonSituations":"Enabling bypass transitions (required by some tree-pattern/XPath use) on generated code from an incompatible tool/runtime pair; hand-modified grammars with left recursion whose generated artifacts were stale.","solutions":["Regenerate lexer/parser with the tool version matching the runtime so left-recursive rule ATNs are well formed.","Only enable generateRuleBypassTransitions on a copy of the options (never on defaultOptions) passed to ATNDeserializer.","If you do not need pattern matching/XPath, leave the option off — the code path is then skipped entirely."],"exampleFix":"# before\nATNDeserializationOptions.defaultOptions.generateRuleBypassTransitions = True  # also read-only error\n\n# after\nopts = ATNDeserializationOptions(ATNDeserializationOptions.defaultOptions)\nopts.generateRuleBypassTransitions = True\natn = ATNDeserializer(opts).deserialize(serialized)","handlingStrategy":"try-catch","validationCode":"opts = ATNDeserializationOptions(ATNDeserializationOptions.defaultOptions)\nif opts.generateRuleBypassTransitions:  # only needed for pattern matching / XPath\n    assert tool_version == runtime_version, 'regenerate grammars before using bypass transitions'","typeGuard":null,"tryCatchPattern":"try:\n    atn = ATNDeserializer(opts).deserialize(data)\nexcept Exception as e:\n    if 'final state of the precedence rule' in str(e):\n        raise RuntimeError('left-recursive ATN malformed: regenerate with matching ANTLR tool version')\n    raise","preventionTips":["Enable generateRuleBypassTransitions only on an options copy and only when you need pattern matching","Regenerate grammars containing left recursion with the matching tool version"],"tags":["antlr4","python","atn","left-recursion","deserialization"],"backgroundTag":null,"analyzedSha":"7d5770395bb7b02eb56e7c62662cb1d7c08f42a3","analyzedAt":"2026-08-14T14:47:56.354Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}