{"id":"1c2c0784d5adde78","repo":"pypa/pip","slug":"url-scheme-must-be-file-when-dir-info-is-presen","errorCode":null,"errorMessage":"URL scheme must be file:// when dir_info is present","messagePattern":"URL scheme must be file:// when dir_info is present","errorType":"validation","errorClass":"DirectUrlValidationError","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/packaging/direct_url.py","lineNumber":281,"sourceCode":"    @classmethod\n    def _from_dict(cls, d: Mapping[str, Any]) -> Self:\n        direct_url = cls(\n            url=_get_required(d, str, \"url\"),\n            archive_info=_get_object(d, ArchiveInfo, \"archive_info\"),\n            vcs_info=_get_object(d, VcsInfo, \"vcs_info\"),\n            dir_info=_get_object(d, DirInfo, \"dir_info\"),\n            subdirectory=_get(d, str, \"subdirectory\"),\n        )\n        if (\n            bool(direct_url.vcs_info)\n            + bool(direct_url.archive_info)\n            + bool(direct_url.dir_info)\n        ) != 1:\n            raise DirectUrlValidationError(\n                \"Exactly one of vcs_info, archive_info, dir_info must be present\"\n            )\n        if direct_url.dir_info is not None and not direct_url.url.startswith(\"file://\"):\n            raise DirectUrlValidationError(\n                \"URL scheme must be file:// when dir_info is present\",\n                context=\"url\",\n            )\n        # XXX subdirectory must be relative, can we, should we validate that here?\n        return direct_url\n\n    @classmethod\n    def from_dict(cls, d: Mapping[str, Any], /) -> Self:\n        \"\"\"Create and validate a DirectUrl instance from a JSON dictionary.\"\"\"\n        return cls._from_dict(d)\n\n    def to_dict(\n        self,\n        *,\n        generate_legacy_hash: bool = False,\n        strip_user_password: bool = True,\n        safe_user_passwords: Collection[str] = (\"git\",),\n    ) -> Mapping[str, Any]:","sourceCodeStart":263,"sourceCodeEnd":299,"githubUrl":"https://github.com/pypa/pip/blob/d7d0d0a39494e28ec1c407bd0680e4a4d1067791/src/pip/_vendor/packaging/direct_url.py#L263-L299","documentation":"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`.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\n{'url': '/home/me/pkg', 'dir_info': {'editable': true}}\n// after\n{'url': 'file:///home/me/pkg', 'dir_info': {'editable': true}}","handlingStrategy":"validation","validationCode":"def coerce_dir_url(url: str) -> str:\n    from urllib.parse import urlsplit\n    p = urlsplit(url)\n    if p.scheme != 'file':\n        raise ValueError(f'dir_info requires file:// URL, got scheme {p.scheme!r}')\n    return url","typeGuard":"def is_file_url(url: object) -> bool:\n    return isinstance(url, str) and url.startswith('file://')","tryCatchPattern":"from packaging.direct_url import DirectUrl, DirectUrlValidationError\ntry:\n    du = DirectUrl.from_dict(d)\nexcept DirectUrlValidationError as e:\n    if 'file://' in str(e):\n        d['url'] = 'file://' + os.path.abspath(d['url'].replace('file://',''))\n    raise","preventionTips":["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"],"tags":["packaging","direct-url","pep610","filesystem","url"],"analyzedSha":"d7d0d0a39494e28ec1c407bd0680e4a4d1067791","analyzedAt":"2026-08-04T20:55:04.259Z","schemaVersion":2}