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
Raised by `DirectUrl._from_dict` when `dir_info` is present (indicating a local-directory install) but the `url` does not start with `file://`. A directory install must reference a local path via the `file://` scheme per PEP 610; any other scheme (https, git+ssh, etc.) is inconsistent with `dir_info`.
Source
Thrown at src/pip/_vendor/packaging/direct_url.py:281
@classmethod
def _from_dict(cls, d: Mapping[str, Any]) -> Self:
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 and not direct_url.url.startswith("file://"):
raise DirectUrlValidationError(
"URL scheme must be file:// 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,
*,
generate_legacy_hash: bool = False,
strip_user_password: bool = True,
safe_user_passwords: Collection[str] = ("git",),
) -> Mapping[str, Any]:View on GitHub (pinned to d7d0d0a394)
Solutions
- Convert the URL to a `file://` path pointing at the local directory, e.g. `file:///abs/path/to/pkg`
- If the install is genuinely remote, remove `dir_info` and use `vcs_info` or `archive_info` instead
- Reinstall the package (`pip install -e <path>` for editable) to regenerate the record
Example fix
// before
{'url': '/home/me/pkg', 'dir_info': {'editable': true}}
// after
{'url': 'file:///home/me/pkg', 'dir_info': {'editable': true}} Defensive patterns
Strategy: validation
Validate before calling
def coerce_dir_url(url: str) -> str:
from urllib.parse import urlsplit
p = urlsplit(url)
if p.scheme != 'file':
raise ValueError(f'dir_info requires file:// URL, got scheme {p.scheme!r}')
return url Type guard
def is_file_url(url: object) -> bool:
return isinstance(url, str) and url.startswith('file://') Try / catch
from packaging.direct_url import DirectUrl, DirectUrlValidationError
try:
du = DirectUrl.from_dict(d)
except DirectUrlValidationError as e:
if 'file://' in str(e):
d['url'] = 'file://' + os.path.abspath(d['url'].replace('file://',''))
raise Prevention
- For editable installs always let pip record the URL
- When writing dir_info records, build URLs with pathlib.Path(...).as_uri()
- Validate scheme == 'file' before persisting dir_info metadata
When it happens
Trigger: A record like `{"url": "https://example.com/pkg", "dir_info": {"editable": true}}` — dir_info implies local but the URL is remote. Also hit when a `pip install -e ./pkg` recorded an incorrect URL scheme.
Common situations: Hand-edited install metadata where someone changed the URL but kept `dir_info`; a buggy installer that wrote the project URL instead of the local file URL; symlinked or relocated editable installs whose path changed.
Related errors
- Unexpected type {type(value).__name__} (expected {expected_t
- Hash values must be strings
- Invalid hash format (expected '<algorithm>=<hash>')
- Algorithm {hash_algorithm!r} used in hash field is not prese
- Algorithm {hash_algorithm!r} used in hash field has differen
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/1c2c0784d5adde78.json.
Report an issue: GitHub.