pypa/pip · error · InstallationError

Directory entries are not supported in remote pylock.toml {p

Error message

Directory entries are not supported in remote pylock.toml {pylock_path_or_url!r}

What it means

InstallationError from package_directory_requirement_url when a pylock.toml fetched from a remote (non-file://) URL contains a directory package entry. A remote lock cannot point the installer at a local source directory the lock author happened to have; pip refuses because directory installs require a real local path.

Source

Thrown at src/pip/_internal/utils/pylock.py:224

) -> 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})"
            )
        url += "#subdirectory=" + package_archive.subdirectory
    return url


def package_directory_requirement_url(
    pylock_path_or_url: str, package_directory: PackageDirectory
) -> str:
    if _is_url(pylock_path_or_url) and not pylock_path_or_url.startswith("file://"):
        raise InstallationError(
            f"Directory entries are not supported in remote pylock.toml "
            f"{pylock_path_or_url!r}"
        )
    url = _package_dist_url(pylock_path_or_url, package_directory.path, None)
    assert url.startswith("file://")
    if not url.endswith("/"):
        url += "/"
    if package_directory.subdirectory:
        url += package_directory.subdirectory
        if not url.endswith("/"):
            url += "/"
    return url


def package_sdist_requirement_url(
    pylock_path_or_url: str, package_sdist: PackageSdist
) -> str:
    return _package_dist_url(pylock_path_or_url, package_sdist.path, package_sdist.url)

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. Remove directory entries from remotely-served locks; replace them with built artifacts referenced by url (wheel/sdist) or VCS url.
  2. Serve the lock file from a local path (or file:// URL) if directory entries are genuinely needed.
  3. Split the lock: a remote lock for binary deps + a local lock for in-tree directory deps.

Example fix

# before - remote lock.toml with a directory entry
# served at https://example.com/lock.toml
[[packages.directory]]
path = "./libs/mypkg"

# after - reference a built artifact instead
[[packages.wheel]]
url = "https://example.com/wheels/mypkg-1.0-py3-none-any.whl"
Defensive patterns

Strategy: validation

Validate before calling

from urllib.parse import urlparse

def remote_lock_has_no_directory_entries(lock_url, packages):
    if urlparse(lock_url).scheme in ('http', 'https'):
        return not any(p.get('type') == 'directory' for p in packages)
    return True
# check before serving a lock over HTTP(S)

Try / catch

try:
    pip_install('-r', lock_url)
except InstallationError as e:
    if 'Directory entries are not supported in remote pylock' in str(e):
        replace_directory_entries_with_wheels(lock_url)
        pip_install('-r', lock_url)
    else:
        raise

Prevention

When it happens

Trigger: _is_url(pylock_path_or_url) is True and the scheme does not start with 'file://', and a package entry is of the directory type (PackageDirectory). E.g. `pip install -r https://example.com/lock.toml` where the lock lists `[[packages.directory]]`.

Common situations: A development lock (containing local editable/directory installs) accidentally published/served over HTTP; a lock generated for an in-repo monorepo consumed by an external CI via URL.

Related errors


AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04). Data as JSON: /data/errors/91e228af85733313.json. Report an issue: GitHub.