python/cpython · error · ValueError
Unsupported major version: {major}
Error message
Unsupported major version: {major} What it means
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.
Source
Thrown at Lib/ast.py:43
def parse(source, filename='<unknown>', mode='exec', *,
type_comments=False, feature_version=None, optimize=-1, module=None):
"""
Parse the source into an AST node.
Equivalent to compile(source, filename, mode, PyCF_ONLY_AST).
Pass type_comments=True to get back type comments where the syntax allows.
"""
flags = PyCF_ONLY_AST
if optimize > 0:
flags |= PyCF_OPTIMIZED_AST
if type_comments:
flags |= PyCF_TYPE_COMMENTS
if feature_version is None:
feature_version = -1
elif isinstance(feature_version, tuple):
major, minor = feature_version # Should be a 2-tuple.
if major != 3:
raise ValueError(f"Unsupported major version: {major}")
feature_version = minor
# Else it should be an int giving the minor version for 3.x.
return compile(source, filename, mode, flags,
_feature_version=feature_version, optimize=optimize,
module=module)
def literal_eval(node_or_string):
"""
Evaluate an expression node or a string containing only a Python
expression. The string or node provided may only consist of the following
Python literal structures: strings, bytes, numbers, tuples, lists, dicts,
sets, booleans, and None.
Caution: A complex expression can overflow the C stack and cause a crash.
"""
if isinstance(node_or_string, str):
node_or_string = parse(node_or_string.lstrip(" \t"), mode='eval').bodyView on GitHub (pinned to bc6749cc3b)
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.
Example fix
# before ast.parse(src, feature_version=(2, 7)) # ValueError # after ast.parse(src, feature_version=(3, 7)) # or feature_version=7 / None
Defensive patterns
Strategy: validation
Validate before calling
def feature_version_of(v):
if v is None:
return None
if isinstance(v, int):
return v
major, minor = v
if major != 3:
raise ValueError(f'only Python 3.x grammars supported, got {v!r}')
return minor
ast.parse(src, feature_version=feature_version_of(cfg_version)) Try / catch
try:
tree = ast.parse(src, feature_version=tuple(map(int, ver.split('.'))))
except ValueError as e:
if 'Unsupported major version' in str(e):
tree = ast.parse(src) # fall back to the running interpreter's grammar Prevention
- 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+$'.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Argument must be an ASCII str
- Invalid isoformat string: {date_string!r}
- Invalid isoformat string: {time_string!r}
- invalid literal for int() with base 10
- malformed node or string: {node!r}
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/5370712288b2b0e2.
Report an issue: GitHub.