{"record":{"id":"d521b401b4ee00a6","repo":"pypa/pip","slug":"expected-whitespace-after-not","errorCode":null,"errorMessage":"Expected whitespace after 'not'","messagePattern":"Expected whitespace after 'not'","errorType":"exception","errorClass":"ParserSyntaxError","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/packaging/_parser.py","lineNumber":410,"sourceCode":"    else:\n        return Variable(env_var)\n\n\ndef process_python_str(python_str: str) -> Value:\n    value = ast.literal_eval(python_str)\n    return Value(str(value))\n\n\ndef _parse_marker_op(tokenizer: Tokenizer) -> Op:\n    \"\"\"\n    marker_op = IN | NOT IN | OP\n    \"\"\"\n    if tokenizer.check(\"IN\"):\n        tokenizer.read()\n        return Op(\"in\")\n    elif tokenizer.check(\"NOT\"):\n        tokenizer.read()\n        tokenizer.expect(\"WS\", expected=\"whitespace after 'not'\")\n        tokenizer.expect(\"IN\", expected=\"'in' after 'not'\")\n        return Op(\"not in\")\n    elif tokenizer.check(\"OP\"):\n        return Op(tokenizer.read().text)\n    else:\n        return tokenizer.raise_syntax_error(\n            \"Expected marker operator, one of <=, <, !=, ==, >=, >, ~=, ===, in, not in\"\n        )\n","sourceCodeStart":392,"sourceCodeEnd":419,"githubUrl":"https://github.com/pypa/pip/blob/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_vendor/packaging/_parser.py#L392-L419","documentation":"Raised as a ParserSyntaxError (surfacing as InvalidMarker) at line 410 inside _parse_marker_op: after consuming a 'not' (NOT token) at line 409, tokenizer.expect(\"WS\", expected=\"whitespace after 'not'\") fails because the token immediately following 'not' is not whitespace. The grammar (line 403-404) requires 'not in' as two whitespace-separated keywords.","triggerScenarios":"A marker using 'not' immediately followed by a non-whitespace token, e.g. 'os_name notin \"Linux\"' where 'notin' is not tokenized as NOT+WS, or 'python_version not)'. The NOT keyword is matched but no WS follows before the expected 'in'.","commonSituations":"Typing 'notin' as one word instead of 'not in'; stripping/normalizing whitespace out of marker strings aggressively so the required space disappears; minified marker strings.","solutions":["Always write the operator as two words with a space: 'not in', e.g. 'os_name not in \"Windows\"'.","Do not strip internal whitespace from marker strings — only trim leading/trailing whitespace.","Validate the marker with packaging.markers.Marker() and read the precise span reported by the error."],"exampleFix":"// before\nMarker('os_name notin \"Windows\"')\n\n// after\nMarker('os_name not in \"Windows\"')","handlingStrategy":"validation","validationCode":"import re\ndef normalize_not_in(marker_str):\n    # collapse accidental 'notin' into 'not in'\n    return re.sub(r'\\bnotin\\b', 'not in', marker_str)","typeGuard":null,"tryCatchPattern":"from packaging.markers import Marker, InvalidMarker\ntry:\n    m = Marker(raw)\nexcept InvalidMarker as e:\n    if \"whitespace after 'not'\" in str(e):\n        raw = normalize_not_in(raw)\n        m = Marker(raw)\n    raise","preventionTips":["Always write 'not in' as two words separated by a space.","Avoid stripping internal whitespace from marker strings."],"tags":["packaging","pep508","marker","whitespace","parser","syntax-error"],"backgroundTag":null,"analyzedSha":"f399c3718970b1b0e2478dac5296eb62679a9b86","analyzedAt":"2026-08-08T23:01:42.227Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}