pypa/pip · error · ParserSyntaxError
Expected whitespace after 'not'
Error message
Expected whitespace after 'not'
What it means
Raised at src/pip/_vendor/packaging/_parser.py:385 inside _parse_marker_op when handling the 'not in' operator. After matching the NOT token (lines 383-384), the grammar requires whitespace before 'in': tokenizer.expect("WS", expected="whitespace after 'not'"). NOT is matched via \bnot\b (_tokenizer.py:59), so the 'not' must be followed by ASCII whitespace; if the next character is anything else (a quote, '(', end-of-input), this fires. It propagates as packaging.markers.InvalidMarker.
Source
Thrown at src/pip/_vendor/packaging/_parser.py:385
else:
return Variable(env_var)
def process_python_str(python_str: str) -> Value:
value = ast.literal_eval(python_str)
return Value(str(value))
def _parse_marker_op(tokenizer: Tokenizer) -> Op:
"""
marker_op = IN | NOT IN | OP
"""
if tokenizer.check("IN"):
tokenizer.read()
return Op("in")
elif tokenizer.check("NOT"):
tokenizer.read()
tokenizer.expect("WS", expected="whitespace after 'not'")
tokenizer.expect("IN", expected="'in' after 'not'")
return Op("not in")
elif tokenizer.check("OP"):
return Op(tokenizer.read().text)
else:
return tokenizer.raise_syntax_error(
"Expected marker operator, one of <=, <, !=, ==, >=, >, ~=, ===, in, not in"
)
View on GitHub (pinned to d7d0d0a394)
Solutions
- Write the operator as two whitespace-separated words: Marker('sys_platform not in "win32"').
- If you meant plain inequality, use '!=' instead: Marker('sys_platform != "win32"').
- Ensure the marker string is not truncated — 'not' must be followed by whitespace and then 'in'.
Example fix
# before
Marker('sys_platform not"win32"')
# after
Marker('sys_platform not in "win32"') Defensive patterns
Strategy: validation
Validate before calling
import re
def normalize_not_in(s: str) -> str:
# Ensure 'not' is followed by whitespace then 'in'.
return re.sub(r'\bnot\s*in\b', 'not in', s) Type guard
from typing import TypeGuard
from pip._vendor.packaging.markers import Marker, InvalidMarker
def is_valid_marker(s: str) -> TypeGuard[str]:
try:
Marker(s)
except InvalidMarker:
return False
return True Try / catch
from pip._vendor.packaging.markers import Marker, InvalidMarker
try:
m = Marker(marker_str)
except InvalidMarker as e:
if "whitespace after 'not'" in str(e):
m = Marker(normalize_not_in(marker_str))
else:
raise Prevention
- Always write the membership operator as two words: 'not in'.
- Prefer '!=' when you simply mean inequality; reserve 'not in' for substring/set membership tests.
- Avoid truncating marker strings mid-operator.
When it happens
Trigger: Marker('sys_platform not"win32"') (quote glued to 'not'), Marker('sys_platform not') ('not' at end of string), Marker('sys_platform not("win32")'), or any marker using 'not in' where the two words are not separated by whitespace.
Common situations: Typing 'notin' as one word (though that usually fails differently as an unknown operator), omitting the space in 'not in', or a truncated marker string ending at 'not'.
Related errors
- Expected end of marker expression
- invalid version: %s
- invalid constraint: %s
- invalid requirement: %s
- unexpected trailing data: %s
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/d521b401b4ee00a6.json.
Report an issue: GitHub.