{"record":{"id":"5370712288b2b0e2","repo":"python/cpython","slug":"unsupported-major-version-major","errorCode":null,"errorMessage":"Unsupported major version: {major}","messagePattern":"Unsupported major version: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"Lib/ast.py","lineNumber":43,"sourceCode":"\ndef parse(source, filename='<unknown>', mode='exec', *,\n          type_comments=False, feature_version=None, optimize=-1, module=None):\n    \"\"\"\n    Parse the source into an AST node.\n    Equivalent to compile(source, filename, mode, PyCF_ONLY_AST).\n    Pass type_comments=True to get back type comments where the syntax allows.\n    \"\"\"\n    flags = PyCF_ONLY_AST\n    if optimize > 0:\n        flags |= PyCF_OPTIMIZED_AST\n    if type_comments:\n        flags |= PyCF_TYPE_COMMENTS\n    if feature_version is None:\n        feature_version = -1\n    elif isinstance(feature_version, tuple):\n        major, minor = feature_version  # Should be a 2-tuple.\n        if major != 3:\n            raise ValueError(f\"Unsupported major version: {major}\")\n        feature_version = minor\n    # Else it should be an int giving the minor version for 3.x.\n    return compile(source, filename, mode, flags,\n                   _feature_version=feature_version, optimize=optimize,\n                   module=module)\n\n\ndef literal_eval(node_or_string):\n    \"\"\"\n    Evaluate an expression node or a string containing only a Python\n    expression.  The string or node provided may only consist of the following\n    Python literal structures: strings, bytes, numbers, tuples, lists, dicts,\n    sets, booleans, and None.\n\n    Caution: A complex expression can overflow the C stack and cause a crash.\n    \"\"\"\n    if isinstance(node_or_string, str):\n        node_or_string = parse(node_or_string.lstrip(\" \\t\"), mode='eval').body","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/ast.py#L25-L61","documentation":"ast.parse accepts feature_version to select the grammar of an older Python 3.x; when it is given as a tuple, only (3, minor) is accepted — any other major version (2, 4, ...) raises ValueError immediately. The value is then reduced to the minor int and forwarded to compile() via _feature_version, so passing a bare non-3 tuple never reaches the compiler.","triggerScenarios":"ast.parse(src, feature_version=(2, 7)) to 'parse Python 2 code'; passing a full sys.version_info-like tuple whose first element is not 3; code that builds the tuple from a config file where the major version is user-controlled.","commonSituations":"Linters/formatters (ast is their backbone) trying to support legacy Python 2 sources; tools that accept a 'python_version' setting and pass it through unvalidated; confusion because feature_version expects (3, minor) or just the minor int, not (major, minor) generally.","solutions":["Use a 3-tuple: feature_version=(3, 9), or pass the minor int directly (feature_version=9), or None for the running interpreter's grammar.","For Python 2 sources use a dedicated tool (e.g. lib2to3-based or external parsers); ast.parse cannot parse Python 2 syntax under any feature_version.","Validate external version inputs before calling ast.parse: require major == 3."],"exampleFix":"# before\nast.parse(src, feature_version=(2, 7))  # ValueError\n\n# after\nast.parse(src, feature_version=(3, 7))  # or feature_version=7 / None","handlingStrategy":"validation","validationCode":"def feature_version_of(v):\n    if v is None:\n        return None\n    if isinstance(v, int):\n        return v\n    major, minor = v\n    if major != 3:\n        raise ValueError(f'only Python 3.x grammars supported, got {v!r}')\n    return minor\n\nast.parse(src, feature_version=feature_version_of(cfg_version))","typeGuard":null,"tryCatchPattern":"try:\n    tree = ast.parse(src, feature_version=tuple(map(int, ver.split('.'))))\nexcept ValueError as e:\n    if 'Unsupported major version' in str(e):\n        tree = ast.parse(src)  # fall back to the running interpreter's grammar","preventionTips":["Accept only (3, minor) or a bare minor int from configuration.","Reject Python-2 targets early with a clear message — ast.parse cannot parse Py2 syntax.","Validate user-supplied version strings with a strict regex like r'^3\\.\\d+$'."],"tags":["ast","parsing","feature-version","valueerror"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}