pypa/pip · error · InstallationError

Directory entries are not supported in remote pylock.toml

Error message

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

What it means

Raised as InstallationError by package_directory_requirement_url (pylock.py:224) when a pylock file loaded from a remote (non-file://) URL contains a package entry of type directory. Directory entries reference local filesystem directories, which only make sense for local lock files. A directory entry in a remote lock file would point to a path on the downloader's machine, which is meaningless or a security risk. The check at line 223 rejects any pylock URL that _is_url and doesn't start with file://.

Solutions

  1. Convert the directory entry to an archive, sdist, or wheel entry with a downloadable URL.
  2. Download the pylock.toml to a local path and reference it by path instead of URL.
  3. Use a file:// URL if the directory is accessible on the local filesystem.
  4. Regenerate the lock file from a clean checkout and replace local directory references with published artifacts.

Example fix

// before (pylock at https://example.com/lock.toml)
[[packages]]
name = "mylib"
[packages.directory]
path = "./mylib"

// after
# Download lock locally first:
$ pip install -r ./lock.toml
Defensive patterns

Strategy: validation

Validate before calling

def is_remote_non_file_url(url: str) -> bool:
    """True if the pylock source is a remote URL (directory entries unsafe)."""
    lowered = url.lower()
    if lowered.startswith('file://'):
        return False
    return lowered.startswith('http://') or lowered.startswith('https://')

# Before installing from a remote pylock, scan for directory entries
# and reject or convert them to archive/wheel entries.

Type guard

def allows_directory_entries(pylock_source: str) -> bool:
    """True if the pylock source allows directory package entries (local only)."""
    return not (
        pylock_source.lower().startswith(('http://', 'https://'))
    )

Prevention

When it happens

Trigger: Loading a pylock.toml from https:// or http:// that contains a [[packages]] entry with a `directory` field (PackageDirectory). The guard at line 223 detects the remote non-file URL and raises.

Common situations: A local lock file (with directory entries) uploaded to a web server or artifact store and then referenced by URL. Lock files generated for monorepo local-development that are accidentally distributed as remote locks.

Related errors


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

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