pypa/pip · error · ParserSyntaxError
Expected URL after @
Error message
Expected URL after @
What it means
Raised as a ParserSyntaxError (surfacing as InvalidRequirement) at line 148 when, after consuming the '@' (AT token) for a direct-URL requirement at line 143-144, the next token is not a URL. The grammar at line 135 requires `AT URL (...)`, so anything that is not a valid URL token after '@' triggers this.
Solutions
- If you meant a version constraint, replace '@' with a proper operator like '>=' or '==' — '@' is only for direct URL references.
- If you meant a direct reference, supply a full URL including scheme, e.g. 'pkg @ https://example.com/pkg-1.0.tar.gz'.
- Validate the URL with urllib.parse first and confirm it starts with a known scheme (http/https/file/git+https).
Example fix
// before
Requirement('mypkg @ 2.0')
// after
Requirement('mypkg @ https://example.com/mypkg-2.0.tar.gz') Defensive patterns
Strategy: validation
Validate before calling
from urllib.parse import urlparse
def looks_like_url_req(s):
# requirement of form 'name @ url'
if '@' not in s.split('[')[0]:
return True # not a direct-url form
url = s.split('@', 1)[1].split(';')[0].strip()
return bool(urlparse(url).scheme) Try / catch
from packaging.requirements import Requirement, InvalidRequirement
try:
req = Requirement(raw)
except InvalidRequirement as e:
if 'URL after @' in str(e):
# user likely used '@' as a version operator
...
raise Prevention
- Use '@' only for direct URL references with a full scheme; use '>=', '==' etc. for versions.
- Validate the URL portion with urlparse before constructing the requirement string.
When it happens
Trigger: A requirement like 'pkg @' with nothing after, or 'pkg @ not-a-url', or 'pkg @ 2.0' (a version where a URL is expected). The tokenizer's URL rule did not match the token following '@'.
Common situations: Confusing a direct URL reference ('pkg @ https://...') with a version specifier ('pkg >= 2.0'); a typo dropping the URL scheme; copying a requirement and truncating it; using '@' as if it were a version operator.
Related errors
- Expected end of dependency specifier
- Expected extra name after comma
- Expected whitespace after URL
- Expected end of marker expression
- Expected whitespace after 'not'
AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08).
Data as JSON: /api/errors/49ab93a002014db4.
Report an issue: GitHub.
Appendix: source
Thrown at src/pip/_vendor/packaging/_parser.py:148
def _parse_requirement_details(
tokenizer: Tokenizer,
) -> tuple[str, str, MarkerList | None]:
"""
requirement_details = AT URL (WS requirement_marker?)?
| specifier WS? (requirement_marker)?
"""
specifier = ""
url = ""
marker = None
if tokenizer.check("AT"):
tokenizer.read()
tokenizer.consume("WS")
url_start = tokenizer.position
url = tokenizer.expect("URL", expected="URL after @").text
if tokenizer.check("END", peek=True):
return (url, specifier, marker)
tokenizer.expect("WS", expected="whitespace after URL")
# The input might end after whitespace.
if tokenizer.check("END", peek=True):
return (url, specifier, marker)
marker = _parse_requirement_marker(
tokenizer,
span_start=url_start,
expected="semicolon (after URL and whitespace)",
)
else:
specifier_start = tokenizer.position
specifier = _parse_specifier(tokenizer)
tokenizer.consume("WS")View on GitHub (pinned to f399c37189)