pypa/pip · error · SyntaxError
invalid URI: %s
Error message
invalid URI: %s
What it means
Raised by distlib's `parse_requirement` for a requirement specified by direct URL (`name @ <url>`): after the `@`, the parser expects a non-space token (matched by `NON_SPACE`); if nothing follows (empty/whitespace after `@`), it raises `SyntaxError: invalid URI`.
Source
Thrown at src/pip/_vendor/distlib/util.py:184
m = IDENTIFIER.match(s)
if not m:
raise SyntaxError('malformed extra: %s' % s)
extras.append(m.groups()[0])
s = s[m.end():]
if not s:
break
if s[0] != ',':
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 = NoneView on GitHub (pinned to d7d0d0a394)
Solutions
- Provide the full URL after `@`: `mypkg @ https://host/path/mypkg-1.0.tar.gz`.
- Ensure any URL variable substituted into the requirement is non-empty.
- Validate direct-URL requirements with `packaging.requirements.Requirement(...)`.
Example fix
# before mypkg @ # after mypkg @ https://files.pythonhosted.org/packages/.../mypkg-1.0-py3-none-any.whl
Defensive patterns
Strategy: validation
Validate before calling
from packaging.requirements import Requirement, InvalidRequirement
def validate_requirement(line: str) -> None:
try:
Requirement(line)
except InvalidRequirement as e:
raise ValueError(f"Bad requirement: {e}") from e Type guard
def direct_url_present(line: str) -> bool:
# if there's an '@' it must be followed by a non-space URL token
import re
return not re.search(r"@\s*$", line.strip()) Try / catch
null
Prevention
- After `name @` always include a full URL token.
- In templates, fail if the URL variable is empty before substitution.
- Validate direct-URL requirements with `packaging.requirements.Requirement`.
When it happens
Trigger: Parsing a requirement like `mypkg @` or `mypkg @ ` (nothing after the `@`). Triggered when pip parses a PEP 508 direct-URL requirement whose URL was omitted.
Common situations: Truncating a direct-URL requirement line; templating that substituted an empty URL variable after `@`; requirements files with incomplete lines.
Related errors
- Invalid URL: %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/7c463b36859835c5.json.
Report an issue: GitHub.