{"id":"51729fa729048d1a","repo":"pypa/pip","slug":"expected-whitespace-after-url","errorCode":null,"errorMessage":"Expected whitespace after URL","messagePattern":"Expected whitespace after URL","errorType":"validation","errorClass":"ParserSyntaxError","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/packaging/_parser.py","lineNumber":144,"sourceCode":"    \"\"\"\n    requirement_details = AT URL (WS requirement_marker?)?\n                        | specifier WS? (requirement_marker)?\n    \"\"\"\n\n    specifier = \"\"\n    url = \"\"\n    marker = None\n\n    if tokenizer.check(\"AT\"):\n        tokenizer.read()\n        tokenizer.consume(\"WS\")\n\n        url_start = tokenizer.position\n        url = tokenizer.expect(\"URL\", expected=\"URL after @\").text\n        if tokenizer.check(\"END\", peek=True):\n            return (url, specifier, marker)\n\n        tokenizer.expect(\"WS\", expected=\"whitespace after URL\")\n\n        # The input might end after whitespace.\n        if tokenizer.check(\"END\", peek=True):\n            return (url, specifier, marker)\n\n        marker = _parse_requirement_marker(\n            tokenizer,\n            span_start=url_start,\n            expected=\"semicolon (after URL and whitespace)\",\n        )\n    else:\n        specifier_start = tokenizer.position\n        specifier = _parse_specifier(tokenizer)\n        tokenizer.consume(\"WS\")\n\n        if tokenizer.check(\"END\", peek=True):\n            return (url, specifier, marker)\n","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/pypa/pip/blob/d7d0d0a39494e28ec1c407bd0680e4a4d1067791/src/pip/_vendor/packaging/_parser.py#L126-L162","documentation":"Raised at src/pip/_vendor/packaging/_parser.py:144. After a URL is read, if the input is not at END (line 141), the grammar requires whitespace before an optional marker clause: tokenizer.expect(\"WS\", expected=\"whitespace after URL\"). WS only matches ASCII space/tab ([ \\t]+, _tokenizer.py:86). It fires when the character immediately after the URL token is neither end-of-input nor an ASCII space/tab.","triggerScenarios":"A direct-URL requirement where content is glued to the URL with a non-ASCII separator (e.g. a non-breaking space U+00A0 or a form-feed) that the WS rule does not recognize, or a programmatically-constructed string that places a token directly after the URL. Note the URL token ([^ \\t]+) is greedy, so in normal prose this branch is rarely reached; when it is, it almost always indicates an unusual/invisible separator character.","commonSituations":"Requirements copy-pasted from rich-text/word processors that insert non-breaking spaces, CSV/templating output that joins fields without a literal space, or test harnesses feeding crafted bytes.","solutions":["Insert a single ASCII space between the URL and any trailing marker: 'pkg @ https://x.org/w ; python_version>=\"3.8\"'.","Sanitize the string by replacing non-ASCII whitespace (\\u00a0, \\u2007, \\u202f, \\ufeff) with a regular space before parsing.","If no marker is intended, ensure nothing follows the URL."],"exampleFix":"# before\nRequirement('mypkg @ https://example.com/x\\u00a0;python_version>=\"3.8\"')\n\n# after\nRequirement('mypkg @ https://example.com/x ; python_version>=\"3.8\"')","handlingStrategy":"validation","validationCode":"import re\n\ndef normalize_requirement_ws(s: str) -> str:\n    # Replace exotic Unicode spaces with a normal space so the WS rule matches.\n    return re.sub(r'[\\u00a0\\u2007\\u202f\\ufeff\\u2003\\u2002]', ' ', s)","typeGuard":"from typing import TypeGuard\nfrom pip._vendor.packaging.requirements import Requirement, InvalidRequirement\n\ndef is_valid_requirement(s: str) -> TypeGuard[str]:\n    try:\n        Requirement(s)\n    except InvalidRequirement:\n        return False\n    return True","tryCatchPattern":"from pip._vendor.packaging.requirements import Requirement, InvalidRequirement\n\ntry:\n    req = Requirement(req_str)\nexcept InvalidRequirement as e:\n    if 'whitespace after URL' in str(e):\n        req = Requirement(normalize_requirement_ws(req_str))  # retry once\n    else:\n        raise","preventionTips":["Always put one ASCII space between a URL and a trailing ';marker'.","Sanitize requirement strings copied from documents/web pages to strip non-breaking spaces before parsing."],"tags":["packaging","parsing","requirements","direct-url","whitespace","unicode"],"analyzedSha":"d7d0d0a39494e28ec1c407bd0680e4a4d1067791","analyzedAt":"2026-08-04T20:55:04.259Z","schemaVersion":2}