pypa/pip · error · InstallationError
The URL 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 VersionControl.get_url_rev_and_auth() when the URL path contains an '@' separator but the revision string after it is empty. The '@' syntax specifies a VCS revision/commit; an empty revision is ambiguous and unsupported.
Solutions
- Remove the trailing '@' from the URL, or add a valid revision after it
- Specify a concrete revision: branch, tag, or commit hash after the '@'
- Validate requirement URLs in your requirements.txt for stray '@' characters
Example fix
// before pip install "git+https://github.com/user/repo.git@" // after # either remove the @ pip install git+https://github.com/user/repo.git # or specify a revision pip install "git+https://github.com/user/repo.git@v1.2.3"
Defensive patterns
Strategy: validation
Validate before calling
import urllib.parse
def vcs_url_has_valid_revision(url: str) -> bool:
path = urllib.parse.urlsplit(url).path
if '@' not in path:
return True
_, _, rev = path.rpartition('@')
return bool(rev)
# before installing
if not vcs_url_has_valid_revision(url):
raise ValueError(f'URL has empty revision after @: {url}') Type guard
def revision_is_nonempty(url_path: str) -> bool:
if '@' not in url_path:
return True
return bool(url_path.rsplit('@', 1)[-1]) Try / catch
from pip._internal.exceptions import InstallationError
try:
url, rev, auth = vc.get_url_rev_and_auth(url)
except InstallationError as e:
if 'empty revision' in str(e):
raise ConfigError(f'remove trailing @ or add a revision: {e}')
raise Prevention
- Avoid trailing '@' in VCS requirement URLs
- Specify concrete revisions (tags, commits, branches) after '@'
- Lint requirements files for stray '@' characters
- Use '#egg=' for the project name, not '@'
When it happens
Trigger: Calling get_url_rev_and_auth(url) where path.rsplit('@', 1) yields an empty rev string. For example, 'git+https://example.com/repo.git@' — the '@' is present but nothing follows it.
Common situations: A typo leaving a trailing '@' in the URL. A requirements file with a URL like 'git+https://.../@' from a botched variable substitution. Copying a URL that had a revision deleted but the '@' left behind.
Related errors
- Sorry, is a malformed VCS url. The format is + :// , e.g…
- {url}
- Absolute paths are not supported in pylock files obtained…
- Badly formatted data
- Can not perform a '--user' install. User site-packages are…
AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08).
Data as JSON: /api/errors/1a226a5f44785e5c.
Report an issue: GitHub.
Appendix: 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 f399c37189)