pypa/pip · error · SyntaxError
Invalid URL: %s
Error message
Invalid URL: %s
What it means
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`.
Source
Thrown at src/pip/_vendor/distlib/util.py:192
raise SyntaxError('comma expected in extras: %s' % s)
s = s[1:].lstrip()
if not extras:
extras = None
if remaining:
if remaining[0] == '@':
# it's a URI
remaining = remaining[1:].lstrip()
m = NON_SPACE.match(remaining)
if not m:
raise SyntaxError('invalid URI: %s' % remaining)
uri = m.groups()[0]
t = urlparse(uri)
# there are issues with Python and URL parsing, so this test
# is a bit crude. See bpo-20271, bpo-23505. Python doesn't
# always parse invalid URLs correctly - it should raise
# exceptions for malformed URLs
if not (t.scheme and t.netloc):
raise SyntaxError('Invalid URL: %s' % uri)
remaining = remaining[m.end():].lstrip()
else:
def get_versions(ver_remaining):
"""
Return a list of operator, version tuples if any are
specified, else None.
"""
m = COMPARE_OP.match(ver_remaining)
versions = None
if m:
versions = []
while True:
op = m.groups()[0]
ver_remaining = ver_remaining[m.end():]
m = VERSION_IDENTIFIER.match(ver_remaining)
if not m:
raise SyntaxError('invalid version: %s' % ver_remaining)View on GitHub (pinned to d7d0d0a394)
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.
Example fix
# before mypkg @ files.pythonhosted.org/packages/.../mypkg-1.0.whl # after mypkg @ https://files.pythonhosted.org/packages/.../mypkg-1.0.whl
Defensive patterns
Strategy: validation
Validate before calling
from urllib.parse import urlparse
def validate_requirement_url(uri: str) -> None:
t = urlparse(uri)
if not (t.scheme and t.netloc):
raise ValueError(f"Invalid URL (missing scheme/netloc): {uri!r}") Type guard
from urllib.parse import urlparse
def is_absolute_url(u: str) -> bool:
t = urlparse(u)
return bool(t.scheme and t.netloc) Try / catch
null
Prevention
- 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.
When it happens
Trigger: 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.
Common situations: Missing `https://` scheme; using bare paths; copy-paste that lost the scheme; using `file:` URLs without the proper `file:///` form; relative URLs in requirements.
Related errors
- invalid URI: %s
- error in string literal: %s
- unterminated string: %s
- unterminated parenthesis: %s
- name expected: %s
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/2970fcc9af452b7a.json.
Report an issue: GitHub.