{"id":"2970fcc9af452b7a","repo":"pypa/pip","slug":"invalid-url-s","errorCode":null,"errorMessage":"Invalid URL: %s","messagePattern":"Invalid URL: (.+?)","errorType":"validation","errorClass":"SyntaxError","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/distlib/util.py","lineNumber":192,"sourceCode":"                raise SyntaxError('comma expected in extras: %s' % s)\n            s = s[1:].lstrip()\n        if not extras:\n            extras = None\n    if remaining:\n        if remaining[0] == '@':\n            # it's a URI\n            remaining = remaining[1:].lstrip()\n            m = NON_SPACE.match(remaining)\n            if not m:\n                raise SyntaxError('invalid URI: %s' % remaining)\n            uri = m.groups()[0]\n            t = urlparse(uri)\n            # there are issues with Python and URL parsing, so this test\n            # is a bit crude. See bpo-20271, bpo-23505. Python doesn't\n            # always parse invalid URLs correctly - it should raise\n            # exceptions for malformed URLs\n            if not (t.scheme and t.netloc):\n                raise SyntaxError('Invalid URL: %s' % uri)\n            remaining = remaining[m.end():].lstrip()\n        else:\n\n            def get_versions(ver_remaining):\n                \"\"\"\n                Return a list of operator, version tuples if any are\n                specified, else None.\n                \"\"\"\n                m = COMPARE_OP.match(ver_remaining)\n                versions = None\n                if m:\n                    versions = []\n                    while True:\n                        op = m.groups()[0]\n                        ver_remaining = ver_remaining[m.end():]\n                        m = VERSION_IDENTIFIER.match(ver_remaining)\n                        if not m:\n                            raise SyntaxError('invalid version: %s' % ver_remaining)","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/pypa/pip/blob/d7d0d0a39494e28ec1c407bd0680e4a4d1067791/src/pip/_vendor/distlib/util.py#L174-L210","documentation":"Raised by distlib's `parse_requirement` for a direct-URL requirement: after extracting the URI token, the parser checks that the URL has both a scheme and netloc (`urlparse(uri)`); if either is missing the URL is not a valid absolute URL and the parser raises `SyntaxError: Invalid URL`.","triggerScenarios":"Parsing a requirement like `mypkg @ hostname/path` (no scheme) or `mypkg @ file.txt` (no `://` netloc). Triggered when pip parses a direct-URL requirement whose URL is not a properly formed absolute URL.","commonSituations":"Missing `https://` scheme; using bare paths; copy-paste that lost the scheme; using `file:` URLs without the proper `file:///` form; relative URLs in requirements.","solutions":["Include the scheme and host: `mypkg @ https://host/path/file.whl`.","For local files use the `file://` form with absolute path: `mypkg @ file:///abs/path/mypkg.whl`.","Validate the URL with `urllib.parse.urlparse` and confirm both `scheme` and `netloc` are non-empty before committing the requirement."],"exampleFix":"# before\nmypkg @ files.pythonhosted.org/packages/.../mypkg-1.0.whl\n\n# after\nmypkg @ https://files.pythonhosted.org/packages/.../mypkg-1.0.whl","handlingStrategy":"validation","validationCode":"from urllib.parse import urlparse\n\ndef validate_requirement_url(uri: str) -> None:\n    t = urlparse(uri)\n    if not (t.scheme and t.netloc):\n        raise ValueError(f\"Invalid URL (missing scheme/netloc): {uri!r}\")","typeGuard":"from urllib.parse import urlparse\ndef is_absolute_url(u: str) -> bool:\n    t = urlparse(u)\n    return bool(t.scheme and t.netloc)","tryCatchPattern":"null","preventionTips":["Always include the scheme (`https://`) and host in direct-URL requirements.","For local files use `file:///abs/path` form.","Validate URLs with `urllib.parse.urlparse` before committing requirements."],"tags":["distlib","requirements-parsing","url","pep508"],"analyzedSha":"d7d0d0a39494e28ec1c407bd0680e4a4d1067791","analyzedAt":"2026-08-04T20:55:04.259Z","schemaVersion":2}