pypa/pip · error · InstallationError

Package URL cannot contain fragments in combination with…

Error message

Package URL {url!r} cannot contain fragments in combination with subdirectory field (in {pylock_path_or_url!r})

What it means

Raised as InstallationError by package_vcs_requirement_url (pylock.py:196) when a VCS package entry in a pylock file already has a # fragment in its constructed URL and also specifies a subdirectory field. URL fragments are used for subdirectory selection, so pip appends #subdirectory=<value> to the VCS URL; if the URL already contains a # (e.g. from a ref or existing fragment), the resulting URL would be ambiguous or malformed. pip refuses rather than produce a broken URL.

Solutions

  1. Remove the # fragment from the VCS URL in the lock file and rely solely on the subdirectory field.
  2. If the fragment encodes a ref/branch, move it into the @revision portion of the VCS URL instead of as a # fragment.
  3. Remove the subdirectory field if the fragment is the intended mechanism for subdirectory selection.
  4. Regenerate the pylock file with `pip lock` so VCS URLs and subdirectory fields are consistent.

Example fix

// before
url = "git+https://repo.git@main#egg=pkg"
subdirectory = "subdir"

// after
url = "git+https://repo.git@main"
subdirectory = "subdir"
Defensive patterns

Strategy: validation

Validate before calling

def validate_vcs_url_with_subdir(url: str, subdirectory: str | None) -> bool:
    """True if a VCS URL + subdirectory combination is safe (no conflicting fragment)."""
    if subdirectory and '#' in url:
        return False  # fragment + subdirectory conflict
    return True

Type guard

def has_fragment_subdir_conflict(url: str, subdirectory: str | None) -> bool:
    """True if the URL has a # fragment AND subdirectory is set (conflict)."""
    return bool(subdirectory) and '#' in url

Prevention

When it happens

Trigger: A pylock VCS package whose dist_url (computed from path/url) already contains a '#', and package_vcs.subdirectory is non-empty. The check at line 195 `if '#' in url` triggers the raise at 196.

Common situations: A VCS URL like git+https://repo.git@branch#egg=pkg combined with a subdirectory field in the same lock entry. A pylock file generated by a tool that didn't strip conflicting fragments. Manually editing a lock file and adding both a fragment and a subdirectory.

Related errors


AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08). Data as JSON: /api/errors/2f6d35d88ef5fb38. Report an issue: GitHub.

Appendix: 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 f399c37189)