pypa/pip · error · InstallationError
The URL {url!r} has an empty revision (after @) which is not
Error message
The URL {url!r} has an empty revision (after @) which is not supported. Include a revision after @ or remove @ from the URL. What it means
Raised by `get_url_rev_and_auth` when a VCS URL contains an `@` separator in the path (the revision delimiter) but the segment after the final `@` is empty. pip interprets `<url>@<rev>` to pin a specific revision; an empty rev (`...@` or `...@#egg=...`) is ambiguous and unsupported, so it aborts with an InstallationError rather than silently installing HEAD.
Source
Thrown at src/pip/_internal/vcs/versioncontrol.py:395
and auth info to use.
Returns: (url, rev, (username, password)).
"""
scheme, netloc, path, query, frag = urllib.parse.urlsplit(url)
if "+" not in scheme:
raise ValueError(
f"Sorry, {url!r} is a malformed VCS url. "
"The format is <vcs>+<protocol>://<url>, "
"e.g. svn+http://myrepo/svn/MyApp#egg=MyApp"
)
# Remove the vcs prefix.
scheme = scheme.split("+", 1)[1]
netloc, user_pass = cls.get_netloc_and_auth(netloc, scheme)
rev = None
if "@" in path:
path, rev = path.rsplit("@", 1)
if not rev:
raise InstallationError(
f"The URL {url!r} has an empty revision (after @) "
"which is not supported. Include a revision after @ "
"or remove @ from the URL."
)
rev = urllib.parse.unquote(rev)
url = urllib.parse.urlunsplit((scheme, netloc, path, query, ""))
return url, rev, user_pass
@staticmethod
def make_rev_args(username: str | None, password: HiddenText | None) -> CommandArgs:
"""
Return the RevOptions "extra arguments" to use in obtain().
"""
return []
def get_url_rev_options(self, url: HiddenText) -> tuple[HiddenText, RevOptions]:
"""
Return the URL and RevOptions object to use in obtain(),View on GitHub (pinned to d7d0d0a394)
Solutions
- Append a concrete revision after `@`, e.g. `git+https://host/repo.git@v1.2.3`.
- If you want the default/HEAD revision, remove the trailing `@` entirely.
- Check that any variable substituted after `@` (tag, commit SHA, branch) is non-empty in your CI/requirements template.
Example fix
# before pip install git+https://github.com/org/repo.git@ # after pip install git+https://github.com/org/repo.git@v1.2.3 # or, for HEAD: pip install git+https://github.com/org/repo.git
Defensive patterns
Strategy: validation
Validate before calling
import urllib.parse
def validate_vcs_rev(url: str) -> None:
path = urllib.parse.urlsplit(url).path
if "@" in path:
rev = path.rsplit("@", 1)[1]
if not rev:
raise ValueError(f"Empty revision after @ in {url!r}") Type guard
null
Try / catch
null
Prevention
- Either omit `@` entirely (for HEAD) or always specify a non-empty rev.
- In CI templates, fail fast if the rev variable is empty before substitution.
- Lint `requirements.txt` for trailing `@` characters.
When it happens
Trigger: Pip parses a VCS URL where `'@' in path` is True, then `path.rsplit('@', 1)` yields an empty rev string. Happens for URLs like `git+https://host/repo.git@` or `svn+https://host/repo/@#egg=Pkg` where nothing follows the `@` before the fragment/query.
Common situations: Typos in `requirements.txt`; copy-pasting a URL and truncating the revision; templating scripts that substitute an empty commit/tag variable after `@`; CI configs where a branch/tag env var is unset.
Related errors
- Sorry, {url!r} is a malformed VCS url. The format is <vcs>+<
- {editable_req} is not a valid editable requirement. It shoul
- Could not detect requirement name for '{editable_req}', plea
- unexpected show-ref line: {line!r}
- Cannot find command {cls.name!r} - invalid PATH
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/1a226a5f44785e5c.json.
Report an issue: GitHub.