pypa/pip · error · DirectUrlValidationError

URL scheme must be file:// when dir_info is present

Error message

URL scheme must be file:// when dir_info is present

What it means

When dir_info is present in a direct URL, the source is a local directory. PEP 610 requires the URL scheme to be file://. If the URL has a different scheme (e.g. https://, git+https://), DirectUrlValidationError is raised because a directory source cannot have a remote URL scheme.

Solutions

  1. If the source is truly a local directory, use a file:// URL (e.g. via pathlib.Path.as_uri())
  2. If the source is remote, use archive_info or vcs_info instead of dir_info
  3. Fix the URL scheme to match the info block type

Example fix

# before
data = {
    "url": "https://example.com/local/project",
    "dir_info": {"editable": True}
}

# after (if truly local)
from pathlib import Path
data = {
    "url": Path("/local/project").as_uri(),
    "dir_info": {"editable": True}
}
# or if remote, use archive_info:
data = {
    "url": "https://example.com/project.tar.gz",
    "archive_info": {"hashes": {"sha256": "..."}}
}
Defensive patterns

Strategy: validation

Validate before calling

import urllib.parse

def validate_dir_info_url(data: dict) -> None:
    if data.get("dir_info") is not None:
        scheme = urllib.parse.urlsplit(data["url"]).scheme
        if scheme != "file":
            raise ValueError(f"dir_info requires file:// URL, got scheme: {scheme!r}")

Type guard

import urllib.parse

def is_valid_dir_url(data: dict) -> bool:
    if data.get("dir_info") is None:
        return True
    return urllib.parse.urlsplit(data["url"]).scheme == "file"

Try / catch

from packaging.direct_url import DirectUrl, DirectUrlValidationError

try:
    du = DirectUrl.from_dict(data)
except DirectUrlValidationError as e:
    if "scheme must be file" in str(e):
        # convert to file URL or switch info block type
        pass

Prevention

When it happens

Trigger: Calling DirectUrl.from_dict(data) with dir_info present but the url field using a non-file scheme: e.g. {'url': 'https://example.com/path', 'dir_info': {'editable': true}}.

Common situations: Using dir_info when the source is actually a remote archive or VCS repo (wrong info block type). Hand-crafted metadata with a mismatched URL scheme and info block.

Related errors


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

Appendix: source

Thrown at src/pip/_vendor/packaging/direct_url.py:300

        direct_url = cls(
            url=_get_required(d, str, "url"),
            archive_info=_get_object(d, ArchiveInfo, "archive_info"),
            vcs_info=_get_object(d, VcsInfo, "vcs_info"),
            dir_info=_get_object(d, DirInfo, "dir_info"),
            subdirectory=_get(d, str, "subdirectory"),
        )
        if (
            bool(direct_url.vcs_info)
            + bool(direct_url.archive_info)
            + bool(direct_url.dir_info)
        ) != 1:
            raise DirectUrlValidationError(
                "Exactly one of vcs_info, archive_info, dir_info must be present"
            )
        if direct_url.dir_info is not None:
            parsed_url = urllib.parse.urlsplit(direct_url.url)
            if parsed_url.scheme != "file":
                raise DirectUrlValidationError(
                    "URL scheme must be file:// when dir_info is present",
                    context="url",
                )
            if not _file_url_has_absolute_path(parsed_url):
                raise DirectUrlValidationError(
                    "File URL must be absolute when dir_info is present",
                    context="url",
                )
        # XXX subdirectory must be relative, can we, should we validate that here?
        return direct_url

    @classmethod
    def from_dict(cls, d: Mapping[str, Any], /) -> Self:
        """Create and validate a DirectUrl instance from a JSON dictionary."""
        return cls._from_dict(d)

    def to_dict(
        self,

View on GitHub (pinned to f399c37189)