pypa/pip · error · ParserSyntaxError
Expected end of marker expression
Error message
Expected end of marker expression
What it means
Raised as a ParserSyntaxError (surfacing as InvalidMarker) at line 315 inside _parse_full_marker: after parsing a complete marker expression via _parse_marker(), tokenizer.expect("END", ...) fails because there is leftover input the marker grammar cannot consume. The parser expected end-of-input but found more tokens.
Solutions
- Inspect repr(marker_str) to find the trailing token and remove or properly join it.
- Ensure multiple marker conditions are joined with 'and'/'or' and parentheses are balanced.
- Validate markers with packaging.markers.Marker() in a test before deployment.
Example fix
// before
Marker('python_version >= "3.0" os_name == "linux"')
// after
Marker('python_version >= "3.0" and os_name == "linux"') Defensive patterns
Strategy: validation
Validate before calling
from packaging.markers import Marker, InvalidMarker
def valid_marker(s):
try:
Marker(s)
return True
except InvalidMarker:
return False Try / catch
from packaging.markers import Marker, InvalidMarker
try:
m = Marker(raw)
except InvalidMarker as e:
raise ValueError(f'bad marker {raw!r}: {e}') from e Prevention
- Join multiple marker conditions with explicit 'and'/'or' and balance parentheses.
- Validate marker strings with Marker() in tests and inspect the reported error span.
When it happens
Trigger: A marker string with trailing content after a valid expression, e.g. 'python_version >= "3.0" extra_token', or an unbalanced/extra clause. After _parse_marker returns, the tokenizer is not at END.
Common situations: Manually constructed marker strings with a stray token; concatenating marker fragments incorrectly; a missing boolean operator between two marker items so the second is left dangling.
Related errors
- Expected whitespace after 'not'
- Expected end of dependency specifier
- Expected extra name after comma
- Expected URL after @
- Expected whitespace after URL
AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08).
Data as JSON: /api/errors/04a64b0fc2b3a91f.
Report an issue: GitHub.
Appendix: source
Thrown at src/pip/_vendor/packaging/_parser.py:315
tokenizer.consume("WS")
if not tokenizer.check("COMMA"):
break
parsed_specifiers += tokenizer.read().text
tokenizer.consume("WS")
return parsed_specifiers
# --------------------------------------------------------------------------------------
# Recursive descent parser for marker expression
# --------------------------------------------------------------------------------------
def parse_marker(source: str) -> MarkerList:
return _parse_full_marker(Tokenizer(source, rules=DEFAULT_RULES))
def _parse_full_marker(tokenizer: Tokenizer) -> MarkerList:
retval = _parse_marker(tokenizer)
tokenizer.expect("END", expected="end of marker expression")
return retval
def _parse_marker(tokenizer: Tokenizer) -> MarkerList:
"""
marker = marker_atom (BOOLOP marker_atom)+
"""
expression = [_parse_marker_atom(tokenizer)]
while tokenizer.check("BOOLOP"):
token = tokenizer.read()
expr_right = _parse_marker_atom(tokenizer)
expression.extend((token.text, expr_right))
return expression
def _parse_marker_atom(tokenizer: Tokenizer) -> MarkerAtom:
"""
marker_atom = WS? LEFT_PARENTHESIS WS? marker WS? RIGHT_PARENTHESIS WS?View on GitHub (pinned to f399c37189)