pypa/pip · error · InstallationError
Package URL {url!r} cannot contain fragments in combination
Error message
Package URL {url!r} cannot contain fragments in combination with subdirectory field (in {pylock_path_or_url!r}) What it means
InstallationError from package_vcs_requirement_url when a VCS package entry in pylock.toml has both a subdirectory field and a URL that already contains a '#' fragment. pip builds the VCS URL as vcs+url@commit and appends '#subdirectory=...'; a pre-existing fragment would make the URL ambiguous/invalid, so it refuses.
Source
Thrown at src/pip/_internal/utils/pylock.py:196
if _is_url(pylock_path_or_url):
raise InstallationError(
f"Absolute paths are not supported in pylock files obtained "
f"from a URL: {path!r} in {pylock_path_or_url!r}"
)
return path_to_url(path)
else:
assert url is not None # guaranteed by packaging.pylock validation
return url
def package_vcs_requirement_url(
pylock_path_or_url: str, package_vcs: PackageVcs
) -> str:
dist_url = _package_dist_url(pylock_path_or_url, package_vcs.path, package_vcs.url)
url = f"{package_vcs.type}+{dist_url}@{package_vcs.commit_id}"
if package_vcs.subdirectory:
if "#" in url:
raise InstallationError(
f"Package URL {url!r} cannot contain fragments in combination "
f"with subdirectory field (in {pylock_path_or_url!r})"
)
url += "#subdirectory=" + package_vcs.subdirectory
return url
def package_archive_requirement_url(
pylock_path_or_url: str, package_archive: PackageArchive
) -> str:
url = _package_dist_url(
pylock_path_or_url, package_archive.path, package_archive.url
)
if package_archive.subdirectory:
if "#" in url:
raise InstallationError(
f"Package URL {url!r} cannot contain fragments in combination "
f"with subdirectory field (in {pylock_path_or_url!r})"View on GitHub (pinned to d7d0d0a394)
Solutions
- Remove the '#...' fragment from the VCS url field; express the subdirectory only via the subdirectory field.
- Drop the subdirectory field if you intend to keep the fragment (not recommended — prefer the field).
- Regenerate the lock with a current tool that emits only the first-class fields.
Example fix
# before [[packages.vcs]] type = "git" url = "https://github.com/o/r.git#subdirectory=pkg" subdirectory = "pkg" # after [[packages.vcs]] type = "git" url = "https://github.com/o/r.git" subdirectory = "pkg"
Defensive patterns
Strategy: validation
Validate before calling
def pylock_vcs_entry_is_clean(url, subdirectory):
return not (subdirectory and '#' in url)
# assert before writing/serving a pylock.toml with a VCS entry Try / catch
try:
pip_install('-r', lock_path)
except InstallationError as e:
if 'cannot contain fragments in combination with subdirectory' in str(e):
strip_fragment_from_vcs_urls(lock_path)
pip_install('-r', lock_path)
else:
raise Prevention
- Express subdirectory via its dedicated field, never via a URL fragment.
- Don't migrate old `#egg=`/`#subdirectory=` VCS URLs verbatim into pylock.
- Regenerate locks with a current tool that emits first-class fields.
When it happens
Trigger: package_vcs.subdirectory is set and the constructed `url` (vcs+dist_url@commit_id) already contains '#'. E.g. a VCS url field ending in '#egg=x' or '#subdirectory=pkg' combined with a subdirectory field on the same entry.
Common situations: Migrating an older requirements-style VCS URL (with #egg= or #subdirectory= fragments) into a pylock.toml but also filling the first-class subdirectory field; tooling that emits both redundantly.
Related errors
- Path {path!r} in pylock file obtained from a URL resolves ou
- Absolute paths are not supported in pylock files obtained fr
- Directory entries are not supported in remote pylock.toml {p
- Error reading pylock file {pylock_path_or_url!r}: {exc}
- Hashes are required in --require-hashes mode, but they are m
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/2f6d35d88ef5fb38.json.
Report an issue: GitHub.